Substrate Bitcoin-Like Blockchain Introduction
This repository is derived from Academy-PoW, originally designed as a learning resource. While implementing a Bitcoin-like blockchain, I faced several challenges that required updates and fixes to ensure smooth execution. Additionally, I transitioned the account model from an Account-based system to a UTXO-based approach. This implementation builds upon the foundational work from Building a UTXO Chain from Scratch | Substrate Seminar. Inspired by this resource, I aimed to further enhance the project, incorporating my own insights and improvements.
Prerequisites
Before starting this interactive tutorial, you should have a basic understanding of Substrate and FRAME development. If you're new to these concepts, it's highly recommended to complete the introductory Substrate tutorials available in the official documentation. For example Substrate Interactive Tutorial - Polkadot Study.
Useful Resources
Here are some essential resources that will assist you throughout this tutorial:
- Set Up Substrate Development Environment – Choose your environment and complete the installation of required packages and Rust.
- Polkadot SDK Documentation – In-depth reference for the Polkadot SDK.
- Simulating a Substrate Network – Learn how to simulate a network in Substrate.
- Unspent Transaction Output (UTXO) – Understanding UTXO account model.
- Proof of Work code breakdown – Learn how the Proof of Work code is implemented.
By following these resources, you’ll gain a solid foundation in Substrate development, enabling you to make the most of this tutorial. 🚀
Overview of Bitcoin-like Blockchain in Substrate we will be building:
graph TD; subgraph UTXO_Runtime Timestamp -->|Time trait| Difficulty Block_Author -->|BlockAuthor| UTXO Bitcoin_Halving --> |Issuance| UTXO FRAME_System subgraph Difficulty Sha3_Difficulty Keccak_Difficulty Md5_Difficulty end end subgraph UTXO_Node subgraph Multi_PoW_Consensus Sha3_Algorithm Keccak_Algorithm Md5_Algorithm end Tx_Pool end Difficulty --> Difficulty_API{{Difficulty API}} Difficulty_API --> Multi_PoW_Consensus UTXO --> Tx_Pool_API{{Tx Pool API}} Tx_Pool_API --> Tx_Pool
Initialize your project
Clone the repository
git clone -b start_here https://github.com/danielbui12/substrate-bitcoin-like-blockchain
Build your project
Let's build your project to verify that your new project is working as expected by running:
cd substrate-bitcoin-like-blockchain
cargo build --release
After build success, please run the node by following command:
❯ ./target/release/academy-pow --dev --tmp
2025-02-10 10:34:04 Academy PoW Chain
2025-02-10 10:34:04 ✌️ version 3.0.0-3289a2a2d00
2025-02-10 10:34:04 ❤️ by @danielbui12, 2025-2025
2025-02-10 10:34:04 📋 Chain specification: Development
2025-02-10 10:34:04 🏷 Node name: broken-class-8644
2025-02-10 10:34:04 👤 Role: AUTHORITY
2025-02-10 10:34:04 💾 Database: ParityDb at /var/folders/n1/4b940c8s1qddng2m8xgxwb280000gn/T/substratea5r5zS/chains/dev/paritydb/full
2025-02-10 10:34:04 🔨 Initializing Genesis block/state (state: 0x6118…d3ad, header-hash: 0xad3e…d8b4)
2025-02-10 10:34:04 Using default protocol ID "sup" because none is configured in the chain specs
2025-02-10 10:34:04 Local node identity is: 12D3KooWQjFVngak2ZeaiTkBonVp5Ju69HGEowLubf7VFWyctFDC
2025-02-10 10:34:04 Running litep2p network backend
2025-02-10 10:34:04 💻 Operating system: macos
2025-02-10 10:34:04 💻 CPU architecture: aarch64
2025-02-10 10:34:04 📦 Highest known block at #0
2025-02-10 10:34:04 〽️ Prometheus exporter started at 127.0.0.1:9615
2025-02-10 10:34:04 Running JSON-RPC server: addr=127.0.0.1:9944, allowed origins=["*"]
2025-02-10 10:34:09 💤 Idle (0 peers), best: #0 (0xad3e…d8b4), finalized #0 (0xad3e…d8b4), ⬇ 0 ⬆ 0
2025-02-10 10:34:14 💤 Idle (0 peers), best: #0 (0xad3e…d8b4), finalized #0 (0xad3e…d8b4), ⬇ 0 ⬆ 0
2025-02-10 10:34:14 🙌 Starting consensus session on top of parent 0xad3e45470dd74feceeecf2e11a9bb18ed97c813c53af0160baea0b81d23fd8b4 (#0)
2025-02-10 10:34:14 🎁 Prepared block for proposing at 1 (2 ms) [hash: 0x4544f634084a6ffad3b6fa3727ad5710bf28337851b003b4d3d14844e7e33ab8; parent_hash: 0xad3e…d8b4; extrinsics (2): [0x1eb2…66b0, 0x6591…3bcd]
2025-02-10 10:34:16 ✅ Successfully mined block on top of: 0xad3e…d8b4
2025-02-10 10:34:16 🏆 Imported #1 (0xad3e…d8b4 → 0xe8ee…1ced)
2025-02-10 10:34:16 🙌 Starting consensus session on top of parent 0xe8eefcc3db4ef32c68907833966ee0fc128098de624b5589409dc0a50dd41ced (#1)
2025-02-10 10:34:16 🎁 Prepared block for proposing at 2 (0 ms) [hash: 0x05b04b1396c7498daf210e8aa512ee1878e083e38b43ddac12be7162e28e6cea; parent_hash: 0xe8ee…1ced; extrinsics (2): [0x132e…94eb, 0x6591…3bcd]
2025-02-10 10:34:19 ✅ Successfully mined block on top of: 0xe8ee…1ced
2025-02-10 10:34:19 🏆 Imported #2 (0xe8ee…1ced → 0x4481…c80e)
2025-02-10 10:34:19 🙌 Starting consensus session on top of parent 0x44812abaf4045df92c9708468fa043274bbf94bfd951c20805411cef23b6c80e (#2)
2025-02-10 10:34:19 🎁 Prepared block for proposing at 3 (0 ms) [hash: 0x04f45b59da4afd41d800d2b24edbaa607d71ab7ee153b92abe6ab86d1d822284; parent_hash: 0x4481…c80e; extrinsics (2): [0x82b2…48d4, 0x6591…3bcd]
2025-02-10 10:34:19 💤 Idle (0 peers), best: #2 (0x4481…c80e), finalized #0 (0xad3e…d8b4), ⬇ 0 ⬆ 0
✅ If you see the above output, then your project is ready to be used. You can now proceed to the next step.
[profile.release]
panic = "unwind"
[workspace]
members = [
"node",
"runtime",
"multi-pow",
]
resolver = "2"
[workspace.dependencies]
# Crates.io dependencies
async-trait = { version = "0.1.53" }
clap = { version = "4.5.3" }
futures = { version = "0.3.30" }
hex = "0.4"
hex-literal = "0.4.1"
jsonrpsee = { version = "0.23.2" }
log = "0.4.25"
md5 = "0.7.0"
parity-scale-codec = { version = "3.1.2", features = [ "derive" ], default-features = false }
rand = { version = "0.8.5", features = [ "small_rng" ] }
scale-info = { version = "2.11.1", default-features = false }
serde = "1.0.137"
serde_json = { version = "1.0.114", default-features = false }
sha3 = "0.10.1"
# Local Dependencies
academy-pow-runtime = { path = "../runtime" }
multi-pow = { default-features = false, path = "../multi-pow" }
# Polkadot SDK Dependencies
frame-executive = { version = "37.0.0", default-features = false }
frame-support = { version = "37.0.0", default-features = false }
frame-system = { version = "37.0.0", default-features = false }
frame-system-rpc-runtime-api = { version = "34.0.0", default-features = false }
pallet-balances = { version = "38.0.0", default-features = false }
pallet-timestamp = { version = "36.0.0", default-features = false }
pallet-transaction-payment = { version = "37.0.0", default-features = false }
pallet-transaction-payment-rpc = { version = "40.0.0" }
pallet-transaction-payment-rpc-runtime-api = { version = "37.0.0", default-features = false }
sc-basic-authorship = { version = "0.44.0" }
sc-chain-spec = { version = "37.0.0" }
sc-cli = { version = "0.46.0", default-features = false }
sc-client-api = { version = "37.0.0" }
sc-consensus = { version = "0.43.0" }
sc-consensus-manual-seal = { version = "0.45.0" }
sc-consensus-pow = { version = "0.43.0" }
sc-executor = { version = "0.40.0" }
sc-network = { version = "0.44.0" }
sc-rpc-api = { version = "0.43.0" }
sc-service = { version = "0.45.0", default-features = false }
sc-telemetry = { version = "24.0.0" }
sc-transaction-pool = { version = "37.0.0" }
sc-transaction-pool-api = { version = "37.0.0" }
sp-api = { version = "34.0.0", default-features = false }
sp-block-builder = { version = "34.0.0", default-features = false }
sp-blockchain = { version = "37.0.0" }
sp-consensus = { version = "0.40.0" }
sp-consensus-pow = { version = "0.40.0", default-features = false }
sp-core = { version = "34.0.0", default-features = false }
sp-genesis-builder = { version = "0.15.0", default-features = false }
sp-inherents = { version = "34.0.0", default-features = false }
sp-io = { version = "38.0.0", default-features = false }
sp-keyring = { version = "39.0.0" }
sp-keystore = { version = "0.40.0" }
sp-offchain = { version = "34.0.0", default-features = false }
sp-runtime = { version = "39.0.0", default-features = false }
sp-session = { version = "35.0.0", default-features = false }
sp-std = { version = "14.0.0", default-features = false }
sp-timestamp = { version = "34.0.0" }
sp-transaction-pool = { version = "34.0.0", default-features = false }
sp-version = { version = "37.0.0", default-features = false }
substrate-build-script-utils = { version = "11.0.0", default-features = false }
substrate-frame-rpc-system = { version = "38.0.0" }
substrate-wasm-builder = { version = "24.0.0", default-features = false }
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>
[package]
authors = [ "@danielbui12" ]
description = "a concrete Substrate PoW algorithm that supports multiple hashing algorithms"
edition = "2021"
name = "multi-pow"
version = "3.0.0"
[dependencies]
md5 = { workspace = true }
parity-scale-codec = { workspace = true }
scale-info = { workspace = true }
sha3 = { workspace = true, optional = true }
sc-client-api = { optional = true, workspace = true }
sc-consensus-pow = { optional = true, workspace = true }
sp-api = { workspace = true }
sp-consensus-pow = { workspace = true }
sp-core = { workspace = true }
sp-runtime = { workspace = true }
log = { workspace = true }
[features]
default = [ "std" ]
std = [
"sha3",
"sc-consensus-pow",
"sc-client-api",
]
#![allow(unused)] fn main() { //! This crate represents a concrete Substrate PoW algorithm. //! //! It is multi-pow in the sense that there are multiple supported hashing algorithms. //! A seal with any of the supported hashing algorithms will be accepted. //! //! The purpose of this design is to demonstrate hard and soft forks by adding and removing valid hashing algorithms. //! While there is no precedent for changing hashing algorithms in the real world yet, it is conceivable that //! a chain may want to upgrade to a new algorithm when the old one is suspected weak. //! In any case, the point is that we want to demonstrate hard and soft forks in an understandable way, //! the multiple hashing algorithms achieves that well. //! //! In the future, the hope is that there will be a dedicated difficulty threshold for each hashing algorithm. //! But currently the Substrate PoW crates are not that flexible. //! We could solve it by adding a pre-digest that includes information about what hashing algo is being used //! for the runtime to use later in the difficulty adjustment. #![cfg_attr(not(feature = "std"), no_std)] use core::str::FromStr; #[cfg(feature = "std")] use std::sync::Arc; use parity_scale_codec::{Decode, Encode}; #[cfg(feature = "std")] use sc_consensus_pow::{Error, PowAlgorithm}; #[cfg(feature = "std")] use sha3::{Digest, Keccak256, Sha3_256}; #[cfg(feature = "std")] use sp_api::ProvideRuntimeApi; #[cfg(feature = "std")] use sp_consensus_pow::DifficultyApi; #[cfg(feature = "std")] use sp_consensus_pow::Seal as RawSeal; use sp_consensus_pow::TotalDifficulty; use sp_core::{H256, U256}; #[cfg(feature = "std")] use sp_runtime::generic::BlockId; #[cfg(feature = "std")] use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; /// A struct that represents a difficulty threshold. /// Unlike a normal PoW algorithm this struct has a separate threshold for each hash #[derive( Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Encode, Decode, Debug, Default, scale_info::TypeInfo, )] pub struct Threshold { pub md5: U256, pub sha3: U256, pub keccak: U256, } // This trait does not seem to be fully baked in the Substrate PoW code // But we do need some kind of sinsible impl here so the node can import blocks. // so I will not use it for now. impl TotalDifficulty for Threshold { fn increment(&mut self, other: Threshold) { self.md5 += other.md5; self.sha3 += other.sha3; self.keccak += other.keccak; } } /// An enum that represents the supported hash types #[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, Debug)] pub enum SupportedHashes { Md5, Sha3, Keccak, } impl Default for SupportedHashes { fn default() -> Self { Self::Sha3 } } /// A struct that represents a concrete hash value tagged with what hashing /// algorithm was used to compute it. #[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, Debug, Default)] pub struct MultiHash { pub algo: SupportedHashes, pub value: H256, } /// Determine whether the given hash satisfies the given difficulty. /// The test is done by multiplying the two together. If the product /// overflows the bounds of U256, then the product (and thus the hash) /// was too high. pub fn simple_hash_meets_difficulty(hash: &H256, difficulty: U256) -> bool { let num_hash = U256::from_little_endian(&hash[..]); let (_, overflowed) = num_hash.overflowing_mul(difficulty); !overflowed } pub fn multi_hash_meets_difficulty(hash: &MultiHash, difficulty: Threshold) -> bool { match hash.algo { SupportedHashes::Md5 => simple_hash_meets_difficulty(&hash.value, difficulty.md5), SupportedHashes::Sha3 => simple_hash_meets_difficulty(&hash.value, difficulty.sha3), SupportedHashes::Keccak => simple_hash_meets_difficulty(&hash.value, difficulty.keccak), } } /// A Seal struct that will be encoded to a Vec<u8> as used as the /// `RawSeal` type. #[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)] pub struct Seal { pub work: MultiHash, pub difficulty: Threshold, pub nonce: U256, } /// A not-yet-computed attempt to solve the proof of work. Calling the /// compute method will compute the hash and return the seal. #[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)] pub struct Compute { pub difficulty: Threshold, pub pre_hash: H256, pub nonce: U256, } #[cfg(feature = "std")] impl Compute { pub fn compute(self, algo: SupportedHashes) -> Seal { let value = match algo { SupportedHashes::Md5 => { // The md5 is only 16 byte output, so we just concatenate it twice to // get an H256 let bytes = *md5::compute(&self.encode()[..]); let mut doubled = [0u8; 32]; doubled[0..16].copy_from_slice(&bytes[0..16]); doubled[16..32].copy_from_slice(&bytes[0..16]); H256::from(doubled) } SupportedHashes::Sha3 => { H256::from_slice(Sha3_256::digest(&self.encode()[..]).as_slice()) } SupportedHashes::Keccak => { H256::from_slice(Keccak256::digest(&self.encode()[..]).as_slice()) } }; Seal { nonce: self.nonce, difficulty: self.difficulty, work: MultiHash { algo, value }, } } } #[cfg(feature = "std")] /// A complete PoW Algorithm that uses multiple hashing algorithms. /// Needs a reference to the client so it can grab the difficulty from the runtime. pub struct MultiPow<C> { client: Arc<C>, fork_config: ForkingConfig, } #[cfg(feature = "std")] impl<C> MultiPow<C> { pub fn new(client: Arc<C>, fork_config: ForkingConfig) -> Self { Self { client, fork_config, } } } //TODO could maybe derive clone_no_bound #[cfg(feature = "std")] impl<C> Clone for MultiPow<C> { fn clone(&self) -> Self { Self::new(self.client.clone(), self.fork_config) } } // Here we implement the general PowAlgorithm trait for our concrete algorithm. #[cfg(feature = "std")] impl<B: BlockT<Hash = H256>, C> PowAlgorithm<B> for MultiPow<C> where C: ProvideRuntimeApi<B>, C::Api: DifficultyApi<B, Threshold>, C: sc_client_api::HeaderBackend<B>, { type Difficulty = Threshold; fn difficulty(&self, parent: B::Hash) -> Result<Self::Difficulty, Error<B>> { let difficulty = self .client .runtime_api() .difficulty(parent) .map_err(|err| { sc_consensus_pow::Error::Environment(format!( "Fetching difficulty from runtime failed: {:?}", err )) })?; Ok(difficulty) } fn verify( &self, parent_id: &BlockId<B>, pre_hash: &H256, pre_digest: Option<&[u8]>, seal: &RawSeal, difficulty: Self::Difficulty, ) -> Result<bool, Error<B>> { // Try to construct a seal object by decoding the raw seal given let seal = match Seal::decode(&mut &seal[..]) { Ok(seal) => seal, Err(_) => return Ok(false), }; log::debug!("✅ Decode seal passed!"); let Some(_encoded_pre_digest) = pre_digest else { return Ok(false); }; log::debug!("✅ Checksum digest passed!"); // // TODO idk why this always return md5 only // let algo_from_predigest = match SupportedHashes::decode(&mut &encoded_pre_digest[..]) { // Ok(algo) => algo, // Err(_) => return Ok(false), // }; // log::debug!("✅ Get algorithm from digest passed!"); // // Check that the pre-digest algo matches the seal algo // // TODO it shouldn't be necessary to have both. // if seal.work.algo != algo_from_predigest { // return Ok(false); // } // log::debug!("✅ Checksum algorithm from seal passed!"); // This is where we handle forks on the verification side. // We will still need to handle it in the mining algorithm somewhere. // Currently we make the miner configure what algo they mine manually with their cli. let parent_number: u32 = match parent_id { BlockId::Hash(h) => *self .client .header(*h) .expect("Database should perform lookup successfully") .expect("parent header should be present in the db") .number(), BlockId::Number(n) => *n, } .try_into() .map_err(|_| ()) .expect("Block numbers can be converted to u32 (because they are u32)"); log::debug!("✅ Checksum parent block number passed!"); // Here we handle the forking logic according the the node operator's request. let valid_algorithm = match self.fork_config { ForkingConfig::Manual => manual_fork_validation(parent_number, seal.work.algo), ForkingConfig::Automatic(fork_heights, maxi_position) => { auto_fork_validation(parent_number, seal.work.algo, fork_heights, maxi_position) } }; if !valid_algorithm { return Ok(false); } log::debug!("✅ Valid algorithm!"); // See whether the hash meets the difficulty requirement. If not, fail fast. if !multi_hash_meets_difficulty(&seal.work, difficulty) { return Ok(false); } log::debug!("✅ Checksum difficulty passed!"); // Make sure the provided work actually comes from the correct pre_hash let compute = Compute { difficulty, pre_hash: *pre_hash, nonce: seal.nonce, }; if compute.compute(seal.work.algo) != seal { return Ok(false); } log::debug!("✅ Re-compute passed!"); log::debug!("🛠️ All passed, append the block to the chain ..."); Ok(true) } } #[derive(Copy, Clone, Debug, Eq, PartialEq)] /// pub struct ForkHeights { /// The block height to perform the soft fork that adds sha3 and keccak support. pub add_sha3_keccak: u32, /// The block height to perform the hard fork that removes md5 support. pub remove_md5: u32, /// The block height to perform the contentious fork where some become sha3- or keccak-maxis. pub split_sha3_keccak: u32, } /// Various political positions a node could take when the network is forking into /// keccak maxis and sha3 maxis #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum MaxiPosition { /// Allow all blocks, both sha3 and keccak NoMaxi, /// Only allow sha3 blocks Sha3Maxi, /// Only allow keccak blocks KeccakMaxi, /// Only allow a single type of blocks. Which type it is is determined by what algo the node is mining. FollowMining, } #[derive(Copy, Clone, Eq, PartialEq)] /// The actual properly typed config after we're done working around all the BS. pub enum ForkingConfig { /// Manual, /// Automatic(ForkHeights, MaxiPosition), } impl FromStr for MaxiPosition { type Err = (); fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(match &s.to_lowercase()[..] { "allow-all" | "allowall" | "no-maxi" | "nomaxi" => Self::NoMaxi, "sha3-maxi" | "sha3maxi" => Self::Sha3Maxi, "keccak-maxi" | "keccakmaxi" => Self::KeccakMaxi, _ => Self::FollowMining, }) } } /// Manual mode, the node operator manually specifies which hashing algorithms are valid through the mining client. /// If you would like to do a fork, simply allow, un-allow some algorithms to check it. fn manual_fork_validation(_parent_number: u32, algo: SupportedHashes) -> bool { use SupportedHashes::*; // To begin with, allow all algorithms. // After the fork height this check is skipped so all the hashes become valid. match algo { Md5 => true, Sha3 => true, Keccak => true, } } /// In automatic mode, the `ForkHeights` and `MaxiPosition` structs define the forking schedule /// and the node's behavior during the contentious fork /// (where the network splits into two chains supporting different hashing algorithms). /// The validation logic considers the parent block height, /// forking configuration parameters, and the hashing algorithm used in the PoW solution to determine its validity. fn auto_fork_validation( parent_number: u32, algo: SupportedHashes, fork_heights: ForkHeights, maxi_position: MaxiPosition, ) -> bool { use MaxiPosition::*; use SupportedHashes::*; log::debug!("parent_number: {:?}", parent_number); log::debug!("fork_heights: {:?}", fork_heights); if parent_number < fork_heights.add_sha3_keccak { // To begin with we only allow md5 hashes for our pow. // After the fork height this check is skipped so all the hashes become valid. match algo { Md5 => true, Sha3 => false, Keccak => false, } } else if parent_number < fork_heights.remove_md5 { // After the first fork, all three algos become valid. match algo { Md5 => true, Sha3 => true, Keccak => true, } } else if parent_number < fork_heights.split_sha3_keccak { // After the second fork, md5 is no longer valid. match algo { Md5 => false, Sha3 => true, Keccak => true, } } else { // Finally we have the contentious fork. // Our behavior here depends which maxi position we have taken. #[allow(clippy::match_like_matches_macro)] match (algo, maxi_position) { (Sha3, Sha3Maxi) => true, (Sha3, NoMaxi) => true, (Keccak, KeccakMaxi) => true, (Keccak, NoMaxi) => true, _ => false, } } } }
[package]
authors = [ "@danielbui12" ]
build = "build.rs"
edition = "2021"
name = "academy-pow"
version = "3.0.0"
[dependencies]
clap = { workspace = true }
futures = { workspace = true }
hex = { workspace = true }
log = { workspace = true }
parity-scale-codec = { workspace = true }
rand = { workspace = true }
serde_json = { workspace = true }
serde = { workspace = true }
sha3 = { workspace = true }
jsonrpsee = { workspace = true }
# Local Dependencies
academy-pow-runtime = { path = "../runtime" }
multi-pow = { path = "../multi-pow" }
# Polkadot SDK
pallet-transaction-payment-rpc = { workspace = true }
sc-rpc-api = { workspace = true }
sp-block-builder = { workspace = true }
substrate-frame-rpc-system = { workspace = true }
sc-basic-authorship = { workspace = true }
sc-chain-spec = { workspace = true }
sc-cli = { workspace = true }
sc-client-api = { workspace = true }
sc-consensus = { workspace = true }
sc-consensus-manual-seal = { workspace = true }
sc-consensus-pow = { workspace = true }
sc-executor = { workspace = true }
sc-network = { workspace = true }
sc-service = { workspace = true }
sc-telemetry = { workspace = true }
sc-transaction-pool = { workspace = true }
sc-transaction-pool-api = { workspace = true }
sp-api = { workspace = true }
sp-blockchain = { workspace = true }
sp-consensus = { workspace = true }
sp-consensus-pow = { workspace = true }
sp-core = { workspace = true }
sp-inherents = { workspace = true }
sp-io = { workspace = true, default-features = true}
sp-keyring = { workspace = true }
sp-runtime = { workspace = true }
sp-timestamp = { workspace = true }
sp-transaction-pool = { workspace = true }
[build-dependencies]
substrate-build-script-utils = { workspace = true }
fn main() { substrate_build_script_utils::generate_cargo_keys(); substrate_build_script_utils::rerun_if_git_head_changed(); }
#![allow(unused)] fn main() { use std::str::FromStr; use academy_pow_runtime::{ AccountId, SS58Prefix, Signature, TOKEN_DECIMALS, TOKEN_SYMBOL, WASM_BINARY, }; use multi_pow::{ForkHeights, ForkingConfig, MaxiPosition}; use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; use sc_service::ChainType; use serde::{Deserialize, Serialize}; use sp_core::{sr25519, Pair, Public}; use sp_core::{ByteArray, H256}; use sp_runtime::traits::{IdentifyAccount, Verify}; /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. pub type ChainSpec = sc_service::GenericChainSpec<ForkingExtensions>; /// PoW and Forking related chain spec extensions to configure the client side forking behavior. /// /// The forks here are all related to adding and removing hash algorithms from the PoW. /// The chain begins supporting only md5. Later is adds sha3 and keccak. Later it removes md5. /// And finally there is a contentious fork where people become maxis. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)] #[serde(deny_unknown_fields)] pub struct ForkingExtensions { /// Manual mode is intended for when we you are running a live workshop. /// No forking happens automatically. Rather, you have to hard-code the forks. /// /// If manual mode is enabled, the rest of the parameters are ignored. /// This should really be an enum, but I have to work around the broken extension system. /// /// Aww damn it! I can't even use bool in this broken system? Okay then I guess 0 means automatic mode /// and anything else means manual mode. pub manual_mode: u32, /// The block height to perform the soft fork that adds sha3 and keccak support. pub add_sha3_keccak: u32, /// The block height to perform the hard fork that removes md5 support. pub remove_md5: u32, /// The block height to perform the contentious fork where some become sha3- or keccak-maxis. pub split_sha3_keccak: u32, // Damn extension thing is so fragile, I can't even use an enum here. // Let alone that time I tried to use the forked value feature. /// The political position that this node will take at the contentious fork. pub maxi_position: String, } impl From<&ForkingExtensions> for ForkingConfig { fn from(e: &ForkingExtensions) -> Self { if e.manual_mode > 0 { return Self::Manual; } let fork_heights = ForkHeights { add_sha3_keccak: e.add_sha3_keccak, remove_md5: e.remove_md5, split_sha3_keccak: e.split_sha3_keccak, }; let maxi_position = MaxiPosition::from_str(&e.maxi_position).expect("Should have a valid maxi position..."); Self::Automatic(fork_heights, maxi_position) } } impl ForkingExtensions { /// Try to get the extension from the given `ChainSpec`. pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> { sc_chain_spec::get_extension(chain_spec.extensions()) } } /// Generate a crypto pair from seed. pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public { TPublic::Pair::from_string(&format!("//{}", seed), None) .expect("static values are valid; qed") .public() } type AccountPublic = <Signature as Verify>::Signer; /// Generate an account ID from seed. pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId where AccountPublic: From<<TPublic::Pair as Pair>::Public>, { AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account() } pub fn development_config() -> Result<ChainSpec, String> { Ok(ChainSpec::builder( WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?, ForkingExtensions { manual_mode: 1, // change this to `0` if you want to try `auto_fork_validation` add_sha3_keccak: 10, remove_md5: 20, split_sha3_keccak: 30, maxi_position: String::from("follow-mining"), }, ) .with_name("Development") .with_id("dev") .with_chain_type(ChainType::Development) .with_genesis_config_patch(genesis( // Pre-funded accounts vec![ get_account_id_from_seed::<sr25519::Public>("Alice"), get_account_id_from_seed::<sr25519::Public>("Bob"), get_account_id_from_seed::<sr25519::Public>("Alice//stash"), get_account_id_from_seed::<sr25519::Public>("Bob//stash"), ], // Initial Difficulty 4_000_000, )) .with_properties(system_properties()) .build()) } pub fn testnet_config() -> Result<ChainSpec, String> { Ok(ChainSpec::builder( WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?, ForkingExtensions { manual_mode: 1, add_sha3_keccak: 0, remove_md5: 0, split_sha3_keccak: 0, maxi_position: String::new(), }, ) .with_name("Testnet") .with_id("testnet") .with_chain_type(ChainType::Local) .with_genesis_config_patch(genesis( vec![ get_account_id_from_seed::<sr25519::Public>("Alice"), get_account_id_from_seed::<sr25519::Public>("Bob"), get_account_id_from_seed::<sr25519::Public>("Alice//stash"), get_account_id_from_seed::<sr25519::Public>("Bob//stash"), ], 4_000_000, )) .with_properties(system_properties()) .build()) } fn genesis( endowed_accounts: Vec<AccountId>, initial_difficulty: u32, ) -> serde_json::Value { serde_json::json!({ "balances": { // Configure endowed accounts with initial balance of 1 << 50. "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 50)).collect::<Vec<_>>(), }, "keccakDifficultyAdjustment": { "initialDifficulty": u32_to_u8_32(initial_difficulty), }, "md5DifficultyAdjustment": { "initialDifficulty": u32_to_u8_32(initial_difficulty), }, "sha3DifficultyAdjustment": { "initialDifficulty": u32_to_u8_32(initial_difficulty), }, }) } /// Convert u32 (default value) to [u8;32] (U256) /// in little-endian format pub fn u32_to_u8_32(num: u32) -> [u8; 32] { let mut result = [0u8; 32]; let bytes = num.to_le_bytes(); result[..4].copy_from_slice(&bytes); result } fn system_properties() -> sc_chain_spec::Properties { let mut properties = sc_chain_spec::Properties::new(); properties.insert("ss58Format".into(), SS58Prefix::get().into()); properties.insert("tokenSymbol".into(), TOKEN_SYMBOL.into()); properties.insert("tokenDecimals".into(), TOKEN_DECIMALS.into()); properties } }
#![allow(unused)] fn main() { use academy_pow_runtime::AccountId; use multi_pow::SupportedHashes; use sc_cli::{ clap::{ArgGroup, Parser}, RunCmd, }; use sc_service::ChainType; use sp_core::{crypto::Ss58Codec, sr25519}; #[derive(Debug, Parser)] #[clap(subcommand_negates_reqs(true), version(env!("SUBSTRATE_CLI_IMPL_VERSION")))] pub struct Cli { #[clap(subcommand)] pub subcommand: Option<Subcommand>, #[command(flatten)] pub pow: AcademyPowCli, #[clap(flatten)] pub run: RunCmd, } #[derive(Debug, Parser, Clone)] #[clap(group(ArgGroup::new("backup")))] pub struct AcademyPowCli { /// Miner's AccountId (base58 encoding of an SR25519 public key) for the block rewards #[clap(long, conflicts_with = "mining_public_key", value_parser = parse_account_id)] pub mining_account_id: Option<AccountId>, /// Miner's hex encoding of the SR25519 public key) for the block rewards #[clap( long, conflicts_with = "mining_account_id", value_parser = parse_sr25519_public_key )] pub mining_public_key: Option<sr25519::Public>, /// The mining algorithm to use #[clap(long, value_parser = parse_algo, default_value = "md5")] pub mining_algo: multi_pow::SupportedHashes, /// whether to use instant seal #[clap(long, default_value = "false")] pub instant_seal: bool, } impl AcademyPowCli { pub fn public_key_bytes(&self, keyring: Option<sp_keyring::Sr25519Keyring>) -> [u8; 32] { match &self.mining_account_id { Some(account_id) => *account_id.as_ref(), None => match self.mining_public_key { Some(public_key) => public_key.0, None => keyring.map(|k| k.to_raw_public()).unwrap_or([0u8; 32]), }, } } } #[derive(Debug, Parser)] pub struct BuildSpecCmd { #[clap(flatten)] pub base: sc_cli::BuildSpecCmd, /// Chain name. #[arg(long, default_value = "Academy PoW")] pub chain_name: String, /// Chain ID is a short identifier of the chain #[arg(long, value_name = "ID", default_value = "academy_pow")] pub chain_id: String, /// AccountIds of the optional rich accounts #[arg(long, value_delimiter = ',', value_parser = parse_account_id, num_args=1..)] pub endowed_accounts: Option<Vec<AccountId>>, /// The type of the chain. Possible values: "dev", "local", "live" (default) #[arg(long, value_name = "TYPE", value_parser = parse_chaintype, default_value = "live")] pub chain_type: ChainType, #[arg(long, default_value = "4000000")] pub initial_difficulty: u32, } fn parse_algo(s: &str) -> Result<SupportedHashes, String> { Ok(match s { "md" | "Md" | "md5" | "Md5" => SupportedHashes::Md5, "sha" | "sha3" | "Sha" | "Sha3" => SupportedHashes::Sha3, "keccak" | "Keccak" => SupportedHashes::Keccak, s => panic!( "Wrong mining algo: {}. Possible values: md5, sha3, keccak", s ), }) } fn parse_chaintype(s: &str) -> Result<ChainType, String> { Ok(match s { "dev" => ChainType::Development, "local" => ChainType::Local, "live" => ChainType::Live, s => panic!("Wrong chain type {} Possible values: dev local live", s), }) } /// Parse AccountId from a string argument passed on the command line. fn parse_account_id(s: &str) -> Result<AccountId, String> { Ok(AccountId::from_string(s) .expect("Passed string is not a bas58 encoding of a sr25519 public key")) } /// Parse sr25519 pubkey from a string argument passed on the command line. fn parse_sr25519_public_key(s: &str) -> Result<sr25519::Public, String> { Ok(sr25519::Public::from_string(s) .expect("Passed string is not a hex encoding of a sr25519 public key")) } #[derive(Debug, clap::Subcommand)] pub enum Subcommand { /// Key management cli utilities #[command(subcommand)] Key(sc_cli::KeySubcommand), /// Build a chain specification. BuildSpec(BuildSpecCmd), /// Validate blocks. CheckBlock(sc_cli::CheckBlockCmd), /// Export blocks. ExportBlocks(sc_cli::ExportBlocksCmd), /// Export the state of a given block into a chain spec. ExportState(sc_cli::ExportStateCmd), /// Import blocks. ImportBlocks(sc_cli::ImportBlocksCmd), /// Remove the whole chain. PurgeChain(sc_cli::PurgeChainCmd), /// Revert the chain to a previous state. Revert(sc_cli::RevertCmd), /// Db meta columns information. ChainInfo(sc_cli::ChainInfoCmd), } }
#![allow(unused)] fn main() { // Copyright 2017-2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Substrate. If not, see <http://www.gnu.org/licenses/>. use academy_pow_runtime::Block; use multi_pow::{ForkingConfig, MaxiPosition}; use sc_cli::SubstrateCli; use sc_service::PartialComponents; use sp_core::sr25519; use crate::{ chain_spec::{self, ForkingExtensions}, cli::{Cli, Subcommand}, service, }; impl SubstrateCli for Cli { fn impl_name() -> String { "Academy PoW Chain".into() } fn impl_version() -> String { env!("SUBSTRATE_CLI_IMPL_VERSION").into() } fn executable_name() -> String { env!("CARGO_PKG_NAME").into() } fn author() -> String { env!("CARGO_PKG_AUTHORS").into() } fn description() -> String { env!("CARGO_PKG_DESCRIPTION").into() } fn support_url() -> String { "https://github.com/danielbui12/substrate-bitcoin-like-blockchain/issues/new".into() } fn copyright_start_year() -> i32 { 2025 } fn load_spec(&self, id: &str) -> Result<Box<dyn sc_service::ChainSpec>, String> { Ok(match id { "" => Box::new(chain_spec::ChainSpec::from_json_bytes( &include_bytes!("../../spec.json")[..], )?), "dev" => Box::new(chain_spec::development_config()?), "local" => Box::new(chain_spec::testnet_config()?), path => Box::new(chain_spec::ChainSpec::from_json_file( std::path::PathBuf::from(path), )?), }) } } /// Parse and run command line arguments pub fn run() -> sc_cli::Result<()> { let cli = Cli::from_args(); match &cli.subcommand { Some(Subcommand::Key(cmd)) => cmd.run(&cli), Some(Subcommand::BuildSpec(cmd)) => { let runner = cli.create_runner(&cmd.base)?; runner.sync_run(|config| cmd.base.run(config.chain_spec, config.network)) } Some(Subcommand::CheckBlock(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { let PartialComponents { client, task_manager, import_queue, .. } = service::new_partial(&config, ForkingConfig::Manual)?; Ok((cmd.run(client, import_queue), task_manager)) }) } Some(Subcommand::ExportBlocks(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { let PartialComponents { client, task_manager, .. } = service::new_partial(&config, ForkingConfig::Manual)?; Ok((cmd.run(client, config.database), task_manager)) }) } Some(Subcommand::ExportState(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { let PartialComponents { client, task_manager, .. } = service::new_partial(&config, ForkingConfig::Manual)?; Ok((cmd.run(client, config.chain_spec), task_manager)) }) } Some(Subcommand::ImportBlocks(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { let PartialComponents { client, task_manager, import_queue, .. } = service::new_partial(&config, ForkingConfig::Manual)?; Ok((cmd.run(client, import_queue), task_manager)) }) } Some(Subcommand::PurgeChain(cmd)) => { let runner = cli.create_runner(cmd)?; runner.sync_run(|config| cmd.run(config.database)) } Some(Subcommand::Revert(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { let PartialComponents { client, task_manager, backend, .. } = service::new_partial(&config, ForkingConfig::Manual)?; Ok((cmd.run(client, backend, None), task_manager)) }) } Some(Subcommand::ChainInfo(cmd)) => { let runner = cli.create_runner(cmd)?; runner.sync_run(|config| cmd.run::<Block>(&config)) } None => { // Get the mining account from the cli let bytes: [u8; 32] = cli.pow.public_key_bytes(cli.run.get_keyring()); let sr25519_public_key = sr25519::Public::from_raw(bytes); let runner = cli.create_runner(&cli.run)?; runner.run_node_until_exit(|config| async move { // Get the forking information from the chain spec extension. // Convert it to a strong type, and fill in the proper maxi position if they are following mining. let forking_extension = ForkingExtensions::try_get(&*config.chain_spec) .expect("Should be able to get the fork config from the extension"); let forking_config = match ForkingConfig::from(forking_extension) { ForkingConfig::Automatic(fork_heights, MaxiPosition::FollowMining) => { let maxi_position = match cli.pow.mining_algo { multi_pow::SupportedHashes::Md5 => MaxiPosition::NoMaxi, multi_pow::SupportedHashes::Sha3 => MaxiPosition::Sha3Maxi, multi_pow::SupportedHashes::Keccak => MaxiPosition::KeccakMaxi, }; ForkingConfig::Automatic(fork_heights, maxi_position) } old_config => old_config, }; service::new_full::<sc_network::Litep2pNetworkBackend>( config, forking_config, //TODO Combine the following three fields into a MiningConfig analogous to the ForkingConfig sr25519_public_key, cli.pow.instant_seal, cli.pow.mining_algo, ) .map_err(sc_cli::Error::Service) }) } } } }
//! Substrate Node Template CLI library. #![warn(missing_docs)] mod chain_spec; #[macro_use] mod service; mod cli; mod command; mod rpc; fn main() -> sc_cli::Result<()> { command::run() }
#![allow(unused)] fn main() { //! A collection of node-specific RPC methods. //! Substrate provides the `sc-rpc` crate, which defines the core RPC layer //! used by Substrate nodes. This file extends those RPC definitions with //! capabilities that are specific to this project's runtime configuration. #![warn(missing_docs)] use std::sync::Arc; use academy_pow_runtime::{opaque::Block, AccountId, Balance, Index}; use jsonrpsee::RpcModule; pub use sc_rpc_api::DenyUnsafe; use sc_transaction_pool_api::TransactionPool; use sp_api::ProvideRuntimeApi; use sp_block_builder::BlockBuilder; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; /// Full client dependencies. pub struct FullDeps<C, P> { /// The client instance to use. pub client: Arc<C>, /// Transaction pool instance. pub pool: Arc<P>, /// Whether to deny unsafe calls pub deny_unsafe: DenyUnsafe, } /// Instantiate all full RPC extensions. pub fn create_full<C, P>( deps: FullDeps<C, P>, ) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>> where C: ProvideRuntimeApi<Block>, C: HeaderBackend<Block> + HeaderMetadata<Block, Error = BlockChainError> + 'static, C: Send + Sync + 'static, C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>, C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>, C::Api: BlockBuilder<Block>, P: TransactionPool + 'static, { use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; use substrate_frame_rpc_system::{System, SystemApiServer}; let mut module = RpcModule::new(()); let FullDeps { client, pool, deny_unsafe, } = deps; module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; module.merge(TransactionPayment::new(client).into_rpc())?; Ok(module) } }
#![allow(unused)] fn main() { //! Service and ServiceFactory implementation. Specialized wrapper over substrate service. use core::clone::Clone; use std::sync::Arc; use academy_pow_runtime::{self, opaque::Block, PreDigest, RuntimeApi}; use multi_pow::{ForkingConfig, MultiPow, SupportedHashes}; use parity_scale_codec::Encode; use sc_consensus::LongestChain; use sc_service::{error::Error as ServiceError, Configuration, PartialComponents, TaskManager}; use sc_telemetry::{Telemetry, TelemetryWorker}; use sp_core::sr25519; pub(crate) type FullClient = sc_service::TFullClient< Block, RuntimeApi, sc_executor::WasmExecutor<sp_io::SubstrateHostFunctions>, >; type FullBackend = sc_service::TFullBackend<Block>; type FullSelectChain = sc_consensus::LongestChain<FullBackend, Block>; type BasicImportQueue = sc_consensus::DefaultImportQueue<Block>; type BoxBlockImport = sc_consensus::BoxBlockImport<Block>; pub type Service = PartialComponents< FullClient, FullBackend, FullSelectChain, BasicImportQueue, sc_transaction_pool::FullPool<Block, FullClient>, (BoxBlockImport, Option<Telemetry>), >; /// Returns most parts of a service. Not enough to run a full chain, /// But enough to perform chain operations like purge-chain #[allow(clippy::type_complexity)] pub fn new_partial( config: &Configuration, fork_config: ForkingConfig, ) -> Result<Service, ServiceError> { let telemetry = config .telemetry_endpoints .clone() .filter(|x| !x.is_empty()) .map(|endpoints| -> Result<_, sc_telemetry::Error> { let worker = TelemetryWorker::new(16)?; let telemetry = worker.handle().new_telemetry(endpoints); Ok((worker, telemetry)) }) .transpose()?; let executor = sc_service::new_wasm_executor::<sp_io::SubstrateHostFunctions>(config); let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::<Block, RuntimeApi, _>( config, telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), executor, )?; let client = Arc::new(client); let telemetry = telemetry.map(|(worker, telemetry)| { task_manager .spawn_handle() .spawn("telemetry", None, worker.run()); telemetry }); let select_chain = LongestChain::new(backend.clone()); let transaction_pool = sc_transaction_pool::BasicPool::new_full( config.transaction_pool.clone(), config.role.is_authority().into(), config.prometheus_registry(), task_manager.spawn_essential_handle(), client.clone(), ); let block_import = sc_consensus_pow::PowBlockImport::new( client.clone(), client.clone(), MultiPow::new(client.clone(), fork_config), 0, // check inherents starting at block 0 select_chain.clone(), move |_, ()| async move { let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); // We don't need the current mining key to check inherents, so we just use a default. // TODO, I don't think we need to do any checking here at all, right? // So can I just remove the author entirely? let author = academy_pow_runtime::block_author::InherentDataProvider(Default::default()); Ok((timestamp, author)) }, ); let import_queue = sc_consensus_pow::import_queue( Box::new(block_import.clone()), None, MultiPow::new(client.clone(), fork_config), &task_manager.spawn_essential_handle(), config.prometheus_registry(), )?; Ok(PartialComponents { client, backend, task_manager, import_queue, keystore_container, select_chain, transaction_pool, other: (Box::new(block_import), telemetry), }) } /// Builds a new service for a full client. pub fn new_full< N: sc_network::NetworkBackend<Block, <Block as sp_runtime::traits::Block>::Hash>, >( config: Configuration, fork_config: ForkingConfig, sr25519_public_key: sr25519::Public, instant_seal: bool, mining_algo: SupportedHashes, ) -> Result<TaskManager, ServiceError> { let sc_service::PartialComponents { client, backend, mut task_manager, import_queue, keystore_container, select_chain, transaction_pool, other: (pow_block_import, mut telemetry), } = new_partial(&config, fork_config)?; let net_config = sc_network::config::FullNetworkConfiguration::< Block, <Block as sp_runtime::traits::Block>::Hash, N, >::new(&config.network); let metrics = sc_network::NotificationMetrics::new(None); let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, net_config, client: client.clone(), transaction_pool: transaction_pool.clone(), spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, warp_sync_params: None, block_relay: None, metrics: metrics, })?; let role = config.role.clone(); let prometheus_registry = config.prometheus_registry().cloned(); let rpc_extensions_builder = { let client = client.clone(); let pool = transaction_pool.clone(); Box::new(move |deny_unsafe, _| { let deps = crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe, }; crate::rpc::create_full(deps).map_err(Into::into) }) }; sc_service::spawn_tasks(sc_service::SpawnTasksParams { network, client: client.clone(), keystore: keystore_container.keystore(), task_manager: &mut task_manager, transaction_pool: transaction_pool.clone(), rpc_builder: rpc_extensions_builder, backend, system_rpc_tx, tx_handler_controller, sync_service: sync_service.clone(), config, telemetry: telemetry.as_mut(), })?; if role.is_authority() { let proposer = sc_basic_authorship::ProposerFactory::new( task_manager.spawn_handle(), client.clone(), transaction_pool.clone(), prometheus_registry.as_ref(), telemetry.as_ref().map(|x| x.handle()), ); // If instant seal is requested, we just start it. Otherwise, we do the full PoW setup. if instant_seal { let params = sc_consensus_manual_seal::InstantSealParams { block_import: client.clone(), env: proposer, client, pool: transaction_pool, select_chain, consensus_data_provider: None, create_inherent_data_providers: move |_, ()| async move { Ok(sp_timestamp::InherentDataProvider::from_system_time()) }, }; let authorship_future = sc_consensus_manual_seal::run_instant_seal(params); task_manager.spawn_essential_handle().spawn_blocking( "instant-seal", None, authorship_future, ); } else { let (mining_worker, mining_worker_task) = sc_consensus_pow::start_mining_worker( Box::new(pow_block_import), client.clone(), select_chain, MultiPow::new(client, fork_config), proposer, sync_service.clone(), sync_service, // Note the mining algorithm in the pre-runtime digest. // This allows us to know which algo it was in the runtime. // TODO This also makes it possible to remove the algo info from // the seal. Some(PreDigest::from((sr25519_public_key.into(), mining_algo)).encode()), // This code is copied from above. Would be better to not repeat it. move |_, ()| async move { let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); // set default `author` following miner specified in CLI let author = academy_pow_runtime::block_author::InherentDataProvider( sr25519_public_key.encode(), ); Ok((timestamp, author)) }, std::time::Duration::from_secs(10), std::time::Duration::from_secs(5), ); task_manager.spawn_essential_handle().spawn_blocking( "pow-miner", Some("pow-mining"), mining_worker_task, ); // Start Mining worker. //TODO Some of this should move into the multi_pow crate. use multi_pow::{multi_hash_meets_difficulty, Compute}; use sp_core::U256; let mut nonce: U256 = U256::from(0); std::thread::spawn(move || loop { let worker = mining_worker.clone(); let metadata = worker.metadata(); if let Some(metadata) = metadata { let compute = Compute { difficulty: metadata.difficulty, pre_hash: metadata.pre_hash, nonce, }; let seal = compute.compute(mining_algo); if multi_hash_meets_difficulty(&seal.work, seal.difficulty) { nonce = U256::from(0); let _ = futures::executor::block_on(worker.submit(seal.encode())); } else { nonce = nonce.saturating_add(U256::from(1)); if nonce == U256::MAX { nonce = U256::from(0); } } } else { std::thread::sleep(std::time::Duration::from_secs(1)); } }); } } network_starter.start_network(); Ok(task_manager) } }
[package]
authors = [ "Parity Technologies <admin@parity.io>", "Joshy Orndorff" ]
edition = "2021"
homepage = "https://substrate.dev"
name = "academy-pow-runtime"
version = "3.0.0"
[dependencies]
async-trait = { optional = true, workspace = true }
parity-scale-codec = { workspace = true }
scale-info = { workspace = true }
serde = { optional = true, workspace = true }
log.workspace = true
frame-executive = { workspace = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
pallet-balances = { workspace = true }
pallet-timestamp = { workspace = true }
pallet-transaction-payment = { workspace = true }
sp-api = { workspace = true }
sp-block-builder = { workspace = true }
sp-consensus-pow = { workspace = true }
sp-core = { workspace = true }
sp-genesis-builder = { workspace = true }
sp-inherents = { workspace = true }
sp-io = { workspace = true }
sp-keystore = { optional = true, workspace = true }
sp-offchain = { workspace = true }
sp-runtime = { workspace = true }
sp-session = { workspace = true }
sp-std = { workspace = true }
sp-transaction-pool = { workspace = true }
sp-version = { workspace = true }
multi-pow = { default-features = false, path = "../multi-pow" }
# Used for the node's RPCs
frame-system-rpc-runtime-api = { workspace = true }
pallet-transaction-payment-rpc-runtime-api = { workspace = true }
[build-dependencies]
substrate-wasm-builder = { workspace = true }
[dev-dependencies]
hex-literal = { workspace = true }
[features]
default = [ "std" ]
std = [
"pallet-balances/std",
"parity-scale-codec/std",
"frame-executive/std",
"frame-support/std",
"serde",
"sp-api/std",
"sp-block-builder/std",
"sp-consensus-pow/std",
"sp-core/std",
"sp-genesis-builder/std",
"sp-inherents/std",
"sp-io/std",
"sp-offchain/std",
"sp-runtime/std",
"sp-session/std",
"sp-std/std",
"sp-transaction-pool/std",
"sp-version/std",
"frame-system/std",
"pallet-timestamp/std",
"async-trait",
"sp-keystore",
"pallet-transaction-payment/std",
"pallet-transaction-payment-rpc-runtime-api/std",
"frame-system-rpc-runtime-api/std",
"multi-pow/std",
]
fn main() { #[cfg(feature = "std")] substrate_wasm_builder::WasmBuilder::new() .with_current_project() .export_heap_base() .import_memory() .build() }
#![allow(unused)] fn main() { //! A difficulty adjustment algorithm (DAA) to keep the block time close to a particular goal //! Cribbed from Kulupu https://github.com/kulupu/kulupu/blob/master/runtime/src/difficulty.rs //! //! It is possible to implement other DAAs such as that of BTC and BCH. This would be an interesting //! and worth-while experiment. The DAAs should be abstracted away with a trait. //! Some ideas: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3410460 use core::cmp::{max, min}; use frame_support::traits::Time; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_core::U256; use sp_runtime::traits::UniqueSaturatedInto; #[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, Debug, MaxEncodedLen, TypeInfo)] pub struct DifficultyAndTimestamp<M> { pub difficulty: Difficulty, pub timestamp: M, } /// Move value linearly toward a goal pub fn damp(actual: u128, goal: u128, damp_factor: u128) -> u128 { (actual + (damp_factor - 1) * goal) / damp_factor } /// Limit value to be within some factor from a goal pub fn clamp(actual: u128, goal: u128, clamp_factor: u128) -> u128 { max(goal / clamp_factor, min(actual, goal * clamp_factor)) } const DIFFICULTY_ADJUST_WINDOW: u128 = 60; type Difficulty = U256; pub use pallet::*; #[frame_support::pallet(dev_mode)] pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use super::*; /// Pallet's configuration trait. #[pallet::config] pub trait Config<I: 'static = ()>: frame_system::Config { /// A Source for timestamp data type TimeProvider: Time; /// The block time that the DAA will attempt to maintain type TargetBlockTime: Get<u128>; /// Dampening factor to use for difficulty adjustment type DampFactor: Get<u128>; /// Clamp factor to use for difficulty adjustment /// Limit value to within this factor of goal. Recommended value: 2 type ClampFactor: Get<u128>; /// The maximum difficulty allowed. Recommended to use u128::max_value() type MaxDifficulty: Get<u128>; /// Minimum difficulty, enforced in difficulty retargetting /// avoids getting stuck when trying to increase difficulty subject to dampening /// Recommended to use same value as DampFactor type MinDifficulty: Get<u128>; /// Now that the pallet is instantiable, we need a way to decide which blocks are /// relevant to this instance. This function does just that. /// /// The default implementation assumes that all blocks are relevant which is what /// you probably want when there is only a single instance. fn relevant_to_this_instance() -> bool { true } } #[pallet::pallet] pub struct Pallet<T, I = ()>(_); type DifficultyList<T, I> = [Option<DifficultyAndTimestamp<<<T as Config<I>>::TimeProvider as Time>::Moment>>; 60]; /// Past difficulties and timestamps, from earliest to latest. #[pallet::storage] pub type PastDifficultiesAndTimestamps<T: Config<I>, I: 'static = ()> = StorageValue<_, DifficultyList<T, I>, ValueQuery, EmptyList<T, I>>; pub struct EmptyList<T: Config<I>, I: 'static = ()>(PhantomData<(T, I)>); impl<T: Config<I>, I> Get<DifficultyList<T, I>> for EmptyList<T, I> { fn get() -> DifficultyList<T, I> { [None; DIFFICULTY_ADJUST_WINDOW as usize] } } /// Current difficulty. #[pallet::storage] #[pallet::getter(fn difficulty)] pub type CurrentDifficulty<T: Config<I>, I: 'static = ()> = StorageValue<_, Difficulty, ValueQuery>; /// Initial difficulty. #[pallet::storage] pub type InitialDifficulty<T: Config<I>, I: 'static = ()> = StorageValue<_, Difficulty, ValueQuery>; #[pallet::genesis_config] pub struct GenesisConfig<T: Config<I>, I: 'static = ()> { pub _ph_data: Option<PhantomData<(T, I)>>, pub initial_difficulty: [u8; 32], // Difficulty = U256 } #[pallet::genesis_build] impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> { fn build(&self) { let initial_difficulty = U256::from_little_endian(&self.initial_difficulty); // Initialize the Current difficulty CurrentDifficulty::<T, I>::put(&initial_difficulty); // Store the initial difficulty in storage because we will need it // during the first DIFFICULTY_ADJUSTMENT_WINDOW blocks (see todo below). InitialDifficulty::<T, I>::put(&initial_difficulty); } } impl<T: Config<I>, I: 'static> Default for GenesisConfig<T, I> { fn default() -> Self { GenesisConfig { _ph_data: Default::default(), initial_difficulty: [0u8; 32], } } } #[pallet::hooks] impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> { fn on_finalize(_n: BlockNumberFor<T>) { // First check if this is block is relevant to this instance of the difficulty adjustment algorithm if !T::relevant_to_this_instance() { return; } let mut data = PastDifficultiesAndTimestamps::<T, I>::get(); for i in 1..data.len() { data[i - 1] = data[i]; } data[data.len() - 1] = Some(DifficultyAndTimestamp { timestamp: T::TimeProvider::now(), difficulty: Self::difficulty(), }); let mut ts_delta = 0; for i in 1..(DIFFICULTY_ADJUST_WINDOW as usize) { let prev: Option<u128> = data[i - 1].map(|d| d.timestamp.unique_saturated_into()); let cur: Option<u128> = data[i].map(|d| d.timestamp.unique_saturated_into()); let delta = match (prev, cur) { (Some(prev), Some(cur)) => cur.saturating_sub(prev), _ => T::TargetBlockTime::get(), }; ts_delta += delta; } if ts_delta == 0 { ts_delta = 1; } let mut diff_sum = U256::zero(); //TODO Could we just initialize every array cell to the initial difficulty to not need the // separate storage item? for item in data.iter().take(DIFFICULTY_ADJUST_WINDOW as usize) { let diff = match item.map(|d| d.difficulty) { Some(diff) => diff, None => InitialDifficulty::<T, I>::get(), }; diff_sum += diff; } if diff_sum < U256::from(T::MinDifficulty::get()) { diff_sum = U256::from(T::MinDifficulty::get()); } // Calculate the average length of the adjustment window let adjustment_window = DIFFICULTY_ADJUST_WINDOW * T::TargetBlockTime::get(); // adjust time delta toward goal subject to dampening and clamping let adj_ts = clamp( damp(ts_delta, adjustment_window, T::DampFactor::get()), adjustment_window, T::ClampFactor::get(), ); // minimum difficulty avoids getting stuck due to dampening let difficulty = min( U256::from(T::MaxDifficulty::get()), max( U256::from(T::MinDifficulty::get()), diff_sum * U256::from(T::TargetBlockTime::get()) / U256::from(adj_ts), ), ); <PastDifficultiesAndTimestamps<T, I>>::put(data); <CurrentDifficulty<T, I>>::put(difficulty); } } } }
#![allow(unused)] #![cfg_attr(not(feature = "std"), no_std)] fn main() { /// A trait for types that can provide the amount of issuance to award to the block /// author for the given block number. pub trait Issuance<BlockNumber, Balance> { fn issuance(block: BlockNumber) -> Balance; } // Minimal implementations for when you don't actually want any issuance impl Issuance<u32, u128> for () { fn issuance(_block: u32) -> u128 { 0 } } impl Issuance<u64, u128> for () { fn issuance(_block: u64) -> u128 { 0 } } /// A type that provides block issuance according to bitcoin's rules /// Initial issuance is 50 / block /// Issuance is cut in half every 210,000 blocks /// cribbed from github.com/Bitcoin-ABC/bitcoin-abc/blob/9c7b12e6f128a59423f4de3d6d4b5231ebe9aac2/src/validation.cpp#L1007 pub struct BitcoinHalving; /// The number of blocks between each halving. const HALVING_INTERVAL: u32 = 210_000; /// The per-block issuance before any halving. Decimal places should be accounted for here. const INITIAL_ISSUANCE: u32 = 50; impl Issuance<u32, u128> for BitcoinHalving { fn issuance(block: u32) -> u128 { let halving = block / HALVING_INTERVAL; // Force block reward to zero when right shift is undefined. if halving >= 64 { return 0; } // Approximately, 600 seconds (or 10 minutes) for a block to be finalized. // Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years. // Divided by 2 using bitwise (INITIAL_ISSUANCE >> halving).into() } } }
#![allow(unused)] fn main() { //! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm. #![cfg_attr(not(feature = "std"), no_std)] // The construct runtime macro does a lot of recursion and requires us to increase the limit to 256. #![recursion_limit = "256"] // Make the WASM binary available. #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); pub use frame_support::{ construct_runtime, parameter_types, derive_impl, traits::{ Currency, EstimateNextNewSession, Imbalance, IsSubType, KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, ValidatorSet, VariantCountOf, }, weights::{ constants::{ BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, }, IdentityFee, Weight, }, Callable, StorageValue, }; use frame_support::{ genesis_builder_helper::{build_state, get_preset}, instances::{Instance1, Instance2, Instance3}, sp_runtime::Perquintill, traits::{ConstU128, ConstU32, ConstU8}, }; use multi_pow::SupportedHashes; pub use pallet_balances::Call as BalancesCall; pub use pallet_timestamp::Call as TimestampCall; use pallet_transaction_payment::{ConstFeeMultiplier, FungibleAdapter, Multiplier}; use parity_scale_codec::Decode; use sp_api::impl_runtime_apis; use sp_consensus_pow::POW_ENGINE_ID; use sp_core::OpaqueMetadata; // A few exports that help ease life for downstream crates. #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; use sp_runtime::{ create_runtime_str, generic, traits::{ AccountIdLookup, BlakeTwo256, Block as BlockT, Bounded, IdentifyAccount, One, Verify, }, transaction_validity::{ InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, }, ApplyExtrinsicResult, DigestItem, MultiSignature, ExtrinsicInclusionMode, }; pub use sp_runtime::{FixedPointNumber, Perbill, Permill}; use sp_std::prelude::*; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; /// An index to a block. pub type BlockNumber = u32; /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. pub type Signature = MultiSignature; /// Some way of identifying an account on the chain. We intentionally make it equivalent /// to the public key of our transaction signing scheme. pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId; /// The type for looking up accounts. We don't expect more than 4 billion of them, but you /// never know... pub type AccountIndex = u32; /// Balance of an account. pub type Balance = u128; /// Index of a transaction in the chain. pub type Nonce = u32; /// Index of a transaction in the chain. pub type Index = u32; /// A hash of some data used by the chain. pub type Hash = sp_core::H256; /// Consensus digest containing block author and supported hash algorithm. pub type PreDigest = (AccountId, SupportedHashes); /// The BlockAuthor trait in `./block_author.rs` pub mod block_author; // /// The Difficulty Adjustment Algorithm in `./difficulty.rs` pub mod difficulty; /// The total issuance and halving time pub mod issuance; /// UTXOs serve as the digital equivalent of change you receive after making a cash purchase pub mod utxo; /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know /// the specifics of the runtime. They can then be made to be agnostic over specific formats /// of data like extrinsics, allowing for them to continue syncing the network through upgrades /// to even the core data structures. pub mod opaque { pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; use super::*; /// Opaque block header type. pub type Header = generic::Header<BlockNumber, BlakeTwo256>; /// Opaque block type. pub type Block = generic::Block<Header, UncheckedExtrinsic>; /// Opaque block identifier type. pub type BlockId = generic::BlockId<Block>; } /// This runtime version. #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("academy-pow"), impl_name: create_runtime_str!("academy-pow"), authoring_version: 1, spec_version: 1, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, state_version: 1, }; /// The version information used to identify this runtime when compiled natively. #[cfg(feature = "std")] pub fn native_version() -> NativeVersion { NativeVersion { runtime_version: VERSION, can_author_with: Default::default(), } } const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); // native chain currency pub const TOKEN_SYMBOL: &str = "Unit"; pub const TOKEN_DECIMALS: u32 = 12; pub const TOKEN: u128 = 10u128.pow(TOKEN_DECIMALS); parameter_types! { pub const BlockHashCount: BlockNumber = 2400; pub const Version: RuntimeVersion = VERSION; /// We allow for 2 seconds of compute with a 6 second average block time. pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::with_sensible_defaults( Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), NORMAL_DISPATCH_RATIO, ); pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); pub const SS58Prefix: u8 = 42; } #[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)] impl frame_system::Config for Runtime { /// The basic call filter to use in dispatchable. type BaseCallFilter = frame_support::traits::Everything; /// Block & extrinsics weights: base values and limits. type BlockWeights = BlockWeights; /// The maximum length of a block (in bytes). type BlockLength = BlockLength; /// The identifier used to distinguish between accounts. type AccountId = AccountId; /// The aggregated dispatch type that is available for extrinsics. type RuntimeCall = RuntimeCall; /// The lookup mechanism to get account ID from whatever is passed in dispatchers. type Lookup = AccountIdLookup<AccountId, ()>; /// The type for hashing blocks and tries. type Hash = Hash; /// The hashing algorithm used. type Hashing = BlakeTwo256; /// The ubiquitous event type. type RuntimeEvent = RuntimeEvent; /// The ubiquitous origin type. type RuntimeOrigin = RuntimeOrigin; /// Maximum number of block number to block hash mappings to keep (oldest pruned first). type BlockHashCount = BlockHashCount; /// The weight of database operations that the runtime can invoke. type DbWeight = RocksDbWeight; /// Version of the runtime. type Version = Version; /// This type is being generated by the construct runtime macro. type PalletInfo = PalletInfo; /// What to do if a new account is created. type OnNewAccount = (); /// What to do if an account is fully reaped from the system. type OnKilledAccount = (); /// The data to be stored in an account. type AccountData = pallet_balances::AccountData<Balance>; /// Weight information for the extrinsics of this pallet. type SystemWeightInfo = (); /// This is used as an identifier of the chain. 42 is the generic substrate prefix. type SS58Prefix = SS58Prefix; /// The set code logic, just the default since we're not a parachain. type OnSetCode = (); type MaxConsumers = frame_support::traits::ConstU32<16>; type Nonce = Nonce; type Block = Block; } parameter_types! { pub const MinimumPeriod: u64 = 1000; } impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = (); type MinimumPeriod = MinimumPeriod; type WeightInfo = (); } impl pallet_balances::Config for Runtime { type MaxLocks = ConstU32<50>; type MaxReserves = (); type ReserveIdentifier = [u8; 8]; /// The type for recording an account's balance. type Balance = Balance; /// The ubiquitous event type. type RuntimeEvent = RuntimeEvent; type DustRemoval = (); type ExistentialDeposit = ConstU128<500>; type AccountStore = System; type WeightInfo = pallet_balances::weights::SubstrateWeight<Runtime>; type FreezeIdentifier = RuntimeFreezeReason; type MaxFreezes = VariantCountOf<RuntimeFreezeReason>; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; } parameter_types! { pub const TargetBlockTime: u128 = 5_000; // Setting min difficulty to damp factor per recommendation pub const DampFactor: u128 = 3; pub const ClampFactor: u128 = 2; pub const MaxDifficulty: u128 = u128::max_value(); } // Helper function to get the current blocks PoW algo from the predigest fn current_blocks_mining_algo() -> SupportedHashes { System::digest() .logs .iter() .find_map(|digest_item| match digest_item { DigestItem::PreRuntime(POW_ENGINE_ID, pre_digest) => { PreDigest::decode(&mut &pre_digest[..]).map(|d| d.1).ok() } _ => None, }) .expect("There should be exactly one pow pre-digest item") } impl difficulty::Config<Instance1> for Runtime { type TimeProvider = Timestamp; type TargetBlockTime = TargetBlockTime; type DampFactor = DampFactor; type ClampFactor = ClampFactor; type MaxDifficulty = MaxDifficulty; type MinDifficulty = DampFactor; fn relevant_to_this_instance() -> bool { current_blocks_mining_algo() == SupportedHashes::Md5 } } impl difficulty::Config<Instance2> for Runtime { type TimeProvider = Timestamp; type TargetBlockTime = TargetBlockTime; type DampFactor = DampFactor; type ClampFactor = ClampFactor; type MaxDifficulty = MaxDifficulty; type MinDifficulty = DampFactor; fn relevant_to_this_instance() -> bool { current_blocks_mining_algo() == SupportedHashes::Sha3 } } impl difficulty::Config<Instance3> for Runtime { type TimeProvider = Timestamp; type TargetBlockTime = TargetBlockTime; type DampFactor = DampFactor; type ClampFactor = ClampFactor; type MaxDifficulty = MaxDifficulty; type MinDifficulty = DampFactor; fn relevant_to_this_instance() -> bool { current_blocks_mining_algo() == SupportedHashes::Keccak } } impl block_author::Config for Runtime { // Each block mined issues 50 new tokens to the miner fn on_author_set(author_account: Self::AccountId) { let issuance = 50 * TOKEN; let _ = Balances::deposit_creating(&author_account, issuance); } } parameter_types! { // This value increases the priority of `Operational` transactions by adding // a "virtual tip" that's equal to the `OperationalFeeMultiplier * final_fee`. // follows polkadot : https://github.com/paritytech/polkadot/blob/9ce5f7ef5abb1a4291454e8c9911b304d80679f9/runtime/polkadot/src/lib.rs#L369 pub const OperationalFeeMultiplier: u8 = 5; // We expect that on average 25% of the normal capacity will be occupied with normal txs. pub const TargetSaturationLevel: Perquintill = Perquintill::from_percent(25); // During 20 blocks the fee may not change more than by 100%. This, together with the // `TargetSaturationLevel` value, results in variability ~0.067. For the corresponding // formulas please refer to Substrate code at `frame/transaction-payment/src/lib.rs`. pub FeeVariability: Multiplier = Multiplier::saturating_from_rational(67, 1000); // Fee should never be lower than the computational cost. pub MinimumMultiplier: Multiplier = Multiplier::one(); pub MaximumMultiplier: Multiplier = Bounded::max_value(); } parameter_types! { pub FeeMultiplier: Multiplier = Multiplier::one(); } impl pallet_transaction_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = FungibleAdapter<Balances, ()>; type OperationalFeeMultiplier = ConstU8<5>; type WeightToFee = IdentityFee<Balance>; type LengthToFee = IdentityFee<Balance>; type FeeMultiplierUpdate = ConstFeeMultiplier<FeeMultiplier>; } construct_runtime!( pub struct Runtime { System: frame_system, Timestamp: pallet_timestamp, Balances: pallet_balances, TransactionPayment: pallet_transaction_payment, Md5DifficultyAdjustment: difficulty::<Instance1>, Sha3DifficultyAdjustment: difficulty::<Instance2>, KeccakDifficultyAdjustment: difficulty::<Instance3>, BlockAuthor: block_author, } ); /// The address format for describing accounts. pub type Address = sp_runtime::MultiAddress<AccountId, ()>; /// Block header type as expected by this runtime. pub type Header = generic::Header<BlockNumber, BlakeTwo256>; /// Block type as expected by this runtime. pub type Block = generic::Block<Header, UncheckedExtrinsic>; /// A Block signed with a Justification pub type SignedBlock = generic::SignedBlock<Block>; /// The SignedExtension to the basic transaction logic. pub type SignedExtra = ( frame_system::CheckNonZeroSender<Runtime>, frame_system::CheckSpecVersion<Runtime>, frame_system::CheckTxVersion<Runtime>, frame_system::CheckGenesis<Runtime>, frame_system::CheckEra<Runtime>, frame_system::CheckNonce<Runtime>, frame_system::CheckWeight<Runtime>, pallet_transaction_payment::ChargeTransactionPayment<Runtime>, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, Block, frame_system::ChainContext<Runtime>, Runtime, AllPalletsWithSystem, >; impl_runtime_apis! { impl sp_api::Core<Block> for Runtime { fn version() -> RuntimeVersion { VERSION } fn execute_block(block: Block) { Executive::execute_block(block) } fn initialize_block(header: &<Block as BlockT>::Header) -> ExtrinsicInclusionMode { Executive::initialize_block(header) } } impl sp_api::Metadata<Block> for Runtime { fn metadata() -> OpaqueMetadata { OpaqueMetadata::new(Runtime::metadata().into()) } fn metadata_at_version(version: u32) -> Option<OpaqueMetadata> { Runtime::metadata_at_version(version) } fn metadata_versions() -> sp_std::vec::Vec<u32> { Runtime::metadata_versions() } } impl sp_block_builder::BlockBuilder<Block> for Runtime { fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult { Executive::apply_extrinsic(extrinsic) } fn finalize_block() -> <Block as BlockT>::Header { Executive::finalize_block() } fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> { data.create_extrinsics() } fn check_inherents( block: Block, data: sp_inherents::InherentData, ) -> sp_inherents::CheckInherentsResult { data.check_extrinsics(&block) } } impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime { fn validate_transaction( source: TransactionSource, tx: <Block as BlockT>::Extrinsic, block_hash: <Block as BlockT>::Hash, ) -> TransactionValidity { Executive::validate_transaction(source, tx, block_hash) } } impl sp_offchain::OffchainWorkerApi<Block> for Runtime { fn offchain_worker(header: &<Block as BlockT>::Header) { Executive::offchain_worker(header) } } impl sp_session::SessionKeys<Block> for Runtime { fn generate_session_keys(_seed: Option<Vec<u8>>) -> Vec<u8> { Vec::new() } fn decode_session_keys( _encoded: Vec<u8>, ) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> { None } } impl sp_consensus_pow::DifficultyApi<Block, multi_pow::Threshold> for Runtime { fn difficulty() -> multi_pow::Threshold { multi_pow::Threshold { md5: Md5DifficultyAdjustment::difficulty(), sha3: Sha3DifficultyAdjustment::difficulty(), keccak: KeccakDifficultyAdjustment::difficulty(), } } } impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime { fn account_nonce(account: AccountId) -> Index { System::account_nonce(account) } } impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime { fn query_info( uxt: <Block as BlockT>::Extrinsic, len: u32, ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> { TransactionPayment::query_info(uxt, len) } fn query_fee_details( uxt: <Block as BlockT>::Extrinsic, len: u32, ) -> pallet_transaction_payment::FeeDetails<Balance> { TransactionPayment::query_fee_details(uxt, len) } fn query_weight_to_fee(weight: Weight) -> Balance { TransactionPayment::weight_to_fee(weight) } fn query_length_to_fee(length: u32) -> Balance { TransactionPayment::length_to_fee(length) } } impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi<Block, Balance, RuntimeCall> for Runtime { fn query_call_info( call: RuntimeCall, len: u32, ) -> pallet_transaction_payment::RuntimeDispatchInfo<Balance> { TransactionPayment::query_call_info(call, len) } fn query_call_fee_details( call: RuntimeCall, len: u32, ) -> pallet_transaction_payment::FeeDetails<Balance> { TransactionPayment::query_call_fee_details(call, len) } fn query_weight_to_fee(weight: Weight) -> Balance { TransactionPayment::weight_to_fee(weight) } fn query_length_to_fee(length: u32) -> Balance { TransactionPayment::length_to_fee(length) } } impl sp_genesis_builder::GenesisBuilder<Block> for Runtime { fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result { build_state::<RuntimeGenesisConfig>(config) } fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> { get_preset::<RuntimeGenesisConfig>(id, |_| None) } fn preset_names() -> Vec<sp_genesis_builder::PresetId> { Default::default() } } } }
#![allow(unused)] fn main() { // We make sure this pallet uses `no_std` for compiling to Wasm. #![cfg_attr(not(feature = "std"), no_std)] use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_core::{ sr25519::{Public, Signature}, ByteArray, H256, H512, }; use sp_runtime::traits::{BlakeTwo256, Hash, SaturatedConversion}; use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; use super::{block_author::BlockAuthor, issuance::Issuance}; pub use pallet::*; #[frame_support::pallet(dev_mode)] pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use super::*; #[pallet::config] pub trait Config: frame_system::Config { } #[pallet::pallet] pub struct Pallet<T>(_); } }
[toolchain]
channel = "stable"
profile = "minimal"
targets = [ "wasm32-unknown-unknown" ]
components = [ "rustfmt", "clippy", "rust-src" ]
edition = "2021"
use_field_init_shorthand = true
reorder_modules = true
imports_granularity = "Crate"
group_imports = "StdExternalCrate"
reorder_imports = true
{
"name": "Development",
"id": "dev",
"chainType": "Development",
"bootNodes": [
"/ip4/127.0.0.1/tcp/30333/p2p/12D3KooWDqY96nhSh1sahtNAZTiXu1KbeKiwsz9Rwowv6bBEUjvZ"
],
"telemetryEndpoints": null,
"protocolId": null,
"properties": {
"ss58Format": 42,
"tokenDecimals": 12,
"tokenSymbol": "Unit"
},
"manual_mode": 1,
"add_sha3_keccak": 10,
"remove_md5": 20,
"split_sha3_keccak": 30,
"maxi_position": "follow-mining",
"codeSubstitutes": {},
"genesis": {
"runtimeGenesis": {
"code": "0x52bc537646db8e0528b52ffd0058244e043e8a45b61153204c434a070534c63d412249e4094823de82c3328b4264ecc48046290950666699ebcdae36560aa2dfe3f34603e6ef9dbf9ab708ca1987befc032db52b5b8cb9c6f74e54951f04ebfeb6b7dc5b6e29a54c3205701383118610737e4e6f9fe0943847ce915b2e03850cd52d39c130e927fdec04b939b9eaa12783b2d99a602885f5d46d1c5e3827d7922fcf66ad0eaa2def0a6f5aae12a00b21ba60b1186a08dbadd9adf6d960d8df1e9d7e5ec8c5b85bb43f7efa4d8f1f095992c4fef8eb47427ceaf3e3626d6df49b7d61d273d76098f4ed1e751924bd82a114eea97396825eb7a41bb5cbd7aa83a44b9ffed2bb4bb2ea96f449e5450f572fdd48dec87117817651972eddcbeae42e46bd5237ea4ae7377f4e5d060ae31c9b691d53fd752e7ee7d2c3ebd6af6523f3ce8d7a08b783c754fc8a533248bae7a1f5f1d0fbcd995f2c837c78c8facd47a84e443434d46ab9b358a1f721cf092d53fd749a14a6bb0d18ea7b3cfbe6d49cee030c593e60e87d18a7bc38f5d2d9a9f7389719791eaefbf8b92f9c4ffd7efcdb1732d4873ce7a5d7cc56adfaf43bbff8b1b33576b1cced179f3b0df82b2885a93eabde5ec1294c45c18f3fce7906409217291843598d7e6ffbfdc2a1bfceb54b933e3cb43f3cf4de43d6fb388f5bf9f29cc7571fcba01efff1b10ceadcc7c7328873962f4f0649a760d8e479c0f07ae637fb621c4f73a3f6a8b39e7a0f18b2bca7de81a1275fd43939f43c3e3bf0f9988378dcfb988342fb3cfe9d57ef79c0e9dcae0e0c4778ceb7daf31cc8dddaeee7bf7d213f771c509fd1ad053234f4dcad70fe0479e81900596852d7938a2bfd32fbc4bc6ce996746ed715252525a5e77671b742fbe17cf99a1d249dfd5eee94546fa75fc83d289ddba539f7ebbad62de937c2e185f3a573a744f3ea469d7dccadccb95de1fc0ccbc71cb479771bcc9f6e7f0343ead6a3cf764d30bcf2d3a35fc969dfec9674ee38c87eba511bc9faf1db8fbb581bf56c5775e9b65d994be9f4e39fcf5d463d72d9379fbb4a9d9fbb087017427421c457376afa49e1be82443668a7de4ec129dc4f70762b3a0349308a675bc210a538e7b2aa7d4182e74b145d6ca334cff09b2ba0a02385a9a71dd41cf142ea470232780184185ae418638507c513a5946eb4c929fb52c4cc9aaa13ac4415177587cb6aad4e70419d78e3f1ac3309092827cc8431040c6aa420c0549b3b43b001871c4314208d0b3ed284a993bd5a6baddb8a3b25405ccf9cabd58b6c692e59a2892f4aa0e0c68d1f086002931db38617a614ecc051c2a94ecf6b3d73dbb43acf5042cca993d99ad5a6a9a649c0c5094a27cc41061733415ec031f2013abed001468d1c16d034297501a28d3a34e860a60a225a5fa6be8cc1b559a283870e432c31075fb144741e9e58232a0f2d58ec310013b6700928428f24988e8823883a2c29690f503d48f530555956cbea2657748cd8d44f3f38363d434985794a635303cad8638b39646083c311302a803567d634a1f540068b2d98a6903a824c7cb2462989246ad8410b0d0108c01b23ca50a20717c030b3c716464c983161cc844113464d183661b8a04456dbb2ac08309b0bd366946eb469365565b602304b69d0d09921862d80ba28830d56f34314173928e110158494a22fd401bd88a20d1b2ce880e10d0030d99930285936de38c38c2d577461218cedd1b427dc585b3779466ada60c49c4d4875d3193710f1a529aa0e78c21b4b84682a83c39821d2e8d1656620f3061b188c98e2fb020b23ab3ae28cf1069d3b4978a147d20d72b44471c513687c38c2cb88a309292ace400307164094d8d2b0b075549d0d8aa1fab56edb139afaa4a63eb1a94f5cd4a737f5094ead434bb10b1a315bdc446921c64892292f5f842883c60b2a3a9bfa010e2a3030b4a19303055f68f981a28d1930c63871861c5e340b67b2f94292521a70944107531163bc14617a4399cce80250441a43a0b1041346d4e9364fd3afb821ab94eae073d615ecb0b085852e1e0b32b05cfe4053060da8a9a52d550c10a3062ca25eb062cd173f30c090c1d02483c0b054617041b6e1754726a09890aa9ed5329b656d32a629386a77268c0e65ceb461c41842c03143a4c91a2931a66ec882091ce4521d159454585281290e8e020c1d41a410c61e4f380dd1a2e95e4175911801c41b766821468ea51456504c6d4b48f50e523b4c6db556bacdd5d31eab8ac50fa82a67283b358d49c9322d592626cbd4e473789e50e2a43426abfdd534fb713b782cf50c9542172937335bf334068e21c4a0b3441b2d421e70861a69e87005174d5c71a782b183c3161cbae020030e60701083c3181ccc8820c51c534b384165431330ca7a2a8b38c2a74d0f347411049b215ec8828820da90654dd34265b554592d73ac4d0247191fa098a206346808c2044735a109ce0b2f382187993972c698a1696cc258cbc6e9299b323d6968d41c5bbfc439930798357a9062822f7470b2a60729dc6072214e1076bc5abbad3843ad80f2b83553926b2ba4b6356a8a789259b4ac96d14ab78d87a9ca839bca03150f552c1ee6788bce227ea03603a2462be28a1f6fb4b85952410f2f506d0861a28d3be69c80421e5ddce1a38939174a2a5a40072f94088385342e6c59f530460c3bcc01e7cb0c53dc913f74b07632245844a569430b9b326ff8e042ce1cd6c30b3ceea45185068d17490655551e75dcb0041e51f87c01070182b87091861c31d6c88007471380d0f420a234cbbed65ab7ad4c98324e659eca9c29035546aacc1425a2b469d21ae20b4e47ded810430e43a0c1860f1e131dca74874cca21ce147aa8f04297252ffc83131d3a56b4d9e386217ab408c20d3cb4b8828928e6b0930412f18e3d6186384b35142147d4173c6e9a5653defcb0b446197a38d9a1076b5ee62821c31b1bc6c0e9be38e2878baa3ab8d0410733e6b0c32db1003b4d26c4d0c3116dea7031f0c0250d0f5be29011790b2930a8e43c41461d632e53f1c3116b20f1c69a357238e9881f9a769062e1ce9914b6a42f669c720013a70e3676b49206516daa9a96d4e8f284d69243ede2af800a616ad0e3e9841cd460284d53d234fb9aa66daf59eb825307f1933de341b1a47caae566e8a90b54444f29437d26cb5c1b5f1366ad952f02d071051b2798f1c61b35306a9f6e2103c391c6134a7c5115449b2c9a688c7bd45a6badd46badb5bf463951eaad6aadb5d695576badb5d6ba02e27ae6466dd49e7c5d9065d0f40976bd71ca9b991b35cb9757a3f2f83abfd65a9ddb153f036b0557de4ff728e865894959d3349b56b7ed0b93a6cd8ab4030b4f61ecf0c296a6a423ece091840964d430b147a6c5844e8b0b33ab554b0c9a961bb22578a4d9a10e17348c60e3b24973420f46acf145ce13577b3114474da3450c41af1843b34ad3fcd05af4509b0c105eaa9af080a389062091841a1c9ed6a803c76441836a82c2276a090a4fabd9cb535252320166a5a6d530656dfd66b7a67f1122e3403b7ffae4dac55e8d1d05cb6a99ac52cc5307f1990eaacefd321203d541fca1f7b5b2ac66331e13ecccf082163fd0410374c21b20da08428d32e4cca7383673cd66b2882d2b4f61ce1c27d8a023a60d573104115ab050c10919ec10e372ddc1b33ffff63050e83f9dfbc51d149d3db41f9ddb45c130fee6a17d766e57e6d467bbac53b7ed0ae357a7ce79edd29cfaaa9dfa4fbba453772eb24fc1500ad06f3ebb15fdc62a2f3a774ab85bd137d0762bba5173bf8cd040c9207ecdb98b6920479f98972edde2b76fd443625edeb010ffcd3efb71dd9a1ea7bc1b39962fce238fa91eba78c24d0a6cc0a85bf9220198363108d1e60535ce80d1fec2467da2b381a114a2af6e41ae5bd3b94b9281b65bd32bc8ddaa6ed4466de56bd541d3a77bed8a4f9dd52e862ae2e9a9ffb44b3e756f17437dc9e1a903f58b83513fd2af09a3ceed8a5fc1d07e687f3acb177b81860833cc9c00668e138cba95af2a26c8e9c20b3266da9002464156b7a6c7292f5cfdf455bfa292929292126c3ab78b3fb41fae7e3a034d308a3f0320298b130a4e860a6a50b37135705e38262e060e866bc6bde3d6b9765c3a2e1dee855bc7b573d1b86b705a3816ae1e9c0a770fee046ec9b271cfb87968516869b4280d4ad341cb41c3417b426b42734243a32da101a121a199d1a4d0d46862b41e341eb41db4233423b422b4278d08ad8c46461ba30da109a105a1c5a07dd166d064d0bc685d3415b416ec1d4d8b6682c6a429694b560f0d052d056d8bddc3d6b16bd82aab869db269582dec16b68d8563c5b06b6c16b60b9bc64a592a6c9485aa715828ac9a8c857a8565c22261cdd814ac0a56c92e5916b23b3237b2363236322d323132303232b22c32aaec8aac8a8c8a4caa6651b1a870aa14554e65a356552dea16f54d1da342519fa84e5434b5891a445da29ea94ad4242a1295897a4435a23e55226a994aa68ea94354a79a43c5a182a934d41baa0df54b9d21b362cad16b681cd48d5987f432599032cc2e9408da0695a26ea816f406ba454b516568a9a6828241a9a06b281bfa45d64246872ac135d125e811d4886c8eb944c3d01d280f14073a359ba05f2614d4cb6482c22077985ea61c198384a18d9829442c2612b10d15621e61edd02b260f354d5635d150273a89a9344f9077ec10991bce866c8c39439646a6c6849a51778ed944b3c896241c3d05f5411641b19047d036d91d59c66e91444c2966145688ac8e6d633e4183904d370e8b82ecd279502ba452d79126c813fa0efb45e351a728183b85f58256416da03a7415d38bb6a2c3a03fd033990a2d437b9970d813e297d8144d9064aa9769029da2a166988e6a28ac141d45abe934d1857842d4427388334419a217ca0697021d33d3cc37620c3586cac47dc9e4cc39e61c4a86e342cb4c1466125a0d1a0d9269c631cf7032705d26173983b4236b3357a828646cfa883633e3482aa69acaa5a65061984b4825a613768c25422e4187984a4c33524d4607b765aa20cf48212c1959878c42d6d156b8773814b81568940dc272a1623412938e2d63f39027dcf81c2b0b5acb232b4e3ac06404274721306172a37c32e58809934d484852b19523119c4c61424496c14847568eaa8cc004e9e8034224197c84e404c90a932752a080a00356a058399a72a464881c6348be9122c5090852a43871801821207d60ca919430f8e8c8034ca82031410ae103508e441024c1602b52a038413a7ac2e4e8c80303906e988a07a64061524295231090c004ca088f34e54884129c74e0f20b3e02610a95294c9c308132c21f8130858a0490a680800a08207c11e9053f610265842f01490a084080e2010f40c98025bb78420584a3274624176ca584a3284cac3039427252e568cad107985829e188832954a058b17254814f6ec122604285099411beca51942a543030058a931040f08007967c520b3e4272f2048a1427216072048223104cc08327471ff880130f0a20b3602b5140380a018429b68234e5a8ca510853a0b8c4620a140f10916d18042856a640a98274e5884909524618c109932a474fa44001012c0a0854a4c07ee4156c658864c34e98fc482b5804264e909c3c71827414011f59c508a04871020509ca142840e41a8e020215291d6072840402108eae1cf990525c858a03388e47523ae004690a083cc0040984a32a509e4c81c28489100ab238d91c29bc543d275d865252fa98248554a9456a8923c46cfaac7ce69c3e73ae563e73e5e3c30753e6cacd9336b3642b294bca719129a5946b6c2a2b8bc5d26cd632a3b466565259a9dce82669db2a2533d35a2d33ad9ce51eb2b69967e55aa7262be59e75c6586bd566adb5e36ab96b9d9c94b2d2cef2d4dab45a96965bd64e4a2b2b308090e5ae922b655a2b9d927665a6dcccd4b2ac9563b6dc9123b7d56a95ccdc525ad949db9c8cb4a3924a4a9b796e32d62375d64a2311da52fefc74d176b5b6a354525a6d2d0001e6eca993b2adb3d68c29a541b6672532ebec69cd4a0750059045daddb4bbb9a6b1bb69ad1c8f94b625677572ad53cacab54a8ebb878b5256cacc1379380043781a4856e66d080098593273a4b5f654ae55ca596bad5c25a5d232b3a4d552090691525ad99b8f6ddbb6ca552929a5cc2c25d72337592b4fe58ca994cc1c2533cbacca8c32d3cb5232cbcacc928747565969ad5556fe6865965c5972adb1da3ab75867ad1cc771cc5c2995b2594a59abb5d6522ae59cdbb66d5c654b66e666e68c6564a6b4d64a25959225951c67bbebba4e4ac9939b93e3b89ec9492aa5a45272524a1963acd33afdf1e3c78f4a679d929394522aada4952995959bb2566b955c6b6539236dda9c0c1c0dd207770955a8887024428d22301fe0a88a079854a182e404042a52a438f941e22a72858a0890287025c002f72c384008569c84e09a94a3101c496172c58908467c7c808a14261f9802e5031fa0224548152748560610c48a102343a280e0a40352a450a94280098ea23041ba72348582a30f08f1ac20598172e5284302c18995294eaa20314182228208545a14b005503c705465842a4e8ea4804024c8932947238ce084c9d1079c54a1826465023ec091085090a680a00a48e4012410140172e52804264853a0541981090856a088f05304890a952a4c9e1c55397a52850a1357022c6062c1511427523ac0e4084a15240258500500972d787264c5ca51951144a00202932a4721a002c25115264eac3c01e188c901a2b81260c108201c59798560ca9114c2076032850a142b558e42c0a4040f40a9721402260a3840021260c193232654904628a10a084c90ae1c012004538e2411dba5394b96ac09d76ec249591a4b92c49234b52649314b9629936a6d9214b364c992b435e1a4a424db84b3648949b1499698254b379624db242966c992949435498a4949dd84b364e926596296d884b3c4269c94254bd26cc249596413ce92254b9624da242966894d9262962c494933296bc249b549969894d44d922227d126cc4d9262524c4a4a924d3829a99b70525252d26c92149362134e8a4d0ed0559199a308569c7079c0d823947cadc4785384963b33341901638f67d8e3937c2d11632708a82cf6702306c63fd288b114c4144d542401638f61d863137b6462f9ec5189fd0afb08ed42c0b387c0de843d56b54b7bf618a75dedacc9ef9ce2a1c36b08a85d9c536f69e171ea4888b478ea18f48ba1a867d0aeebd435d802a3de41bb7e38750f3e907aea1be802a3fea45d3e9c7a0823b46be5d491aed079ea5868d8d22e204b13c8d024f5d423987eb99292120c30ea514cbb7a9c7a74eaa7760531d3ae78468c31d0b0a0a65d2ca97e0d4db54b88538f5c4437673cf5f8a60518f508a75d439c7aac8a73da45c4a9473ab10e1c4f3ddae9174351ef2dedb24ebd97989aa69e7a83e91960d45b4c3b3df599169e7aa3e92f30eaada65d9b536fa97e79d47baa5d9e536f2eda8d134fbddfb48be7a9cbd152f49e37d5cfbf5cc5c4ceb36a5cd7099b2be658228c26d4a05344d585176f30f51046cd6fee40edfaf1cd5bf2159986dae5e39b232132e137c7a05ddb6f6eb4b906db05bf6dbe817615f1cd9bc857a4da369edfe0b0e337ee370bc061840d3ecc41451c5cc036d6e20a3bd480c9630d15bc806d1cc56f1e99e42b2a356d467cf328a65df3b7375878235cc08c1871ef8d32de88af8cc0a1c41bf11f23fe5dcc88ab91c57b3eed1a41bce71c0a30cf572c22def31f6f8d3dde7320af6aeabd2698e75555bd27f59e1bb1f1f49e67d02ee6e23dd7a08d2def39043a681743b5f1e63df7c0f30fdac5506e10f19e6fc0f3267196def327ed6237ef7908ed62a83854efb994767defb95117f3fa8b48b1020f2e4f67f450c514ac481e64e04c51c70a2db4b10656c45945d4c0e28b7803ba58111fb768b0791fe75480f9f80a8d2ede678ff781b3c5fb78f13e6904f13e64603e3e6978f13e5fbc8f1a64dec73368579033e6781f674017f3f9f9f9f93943cefff802bad88f63d0c5b63a9a78cd354d7396e6ae796ba85d3fae39923aec786d82d7344df30f36d02ecf356fa27908ed2ae29a4bd11ce98ad292e6e39a46816b1ec5689aa6699a11d73c4e69ee9ac737765cf1eeee1774313762c48811235ed4c58c5060470b4f812ba08b51607d7c7c7c7c9c48e88bd451c517f157172be2799ee7799ee779eeea629e16ff43071bffe309e8623f766699f3a50a38e438038a09b6cd110397281fdad8a07406b6396bdbb66ddbe848e33747d2c5360474318d0e2d5b1725d694f1c31033f4f0260319d684f182aa88263b30eb2ceb6ebd3544d4c25bc7a05d3e6ff740838c39527138a17103665d03f9ba600827346e9c00c5431798f50edad580b7fe81f526f2d530eb218c20e6ad235d515a626a0a139d9c78eb714c7cb21ecf588f50f1ad8fdb08b31eddc8d7845925dec77d7c7c7cdc822ee6e325a1a9418535d0c479a20a3a30cf0a1382f07286871cf07001e631e03d3a7278cf0fd0c5bc6ddbb66ddbb66df3a12eb649cd3d04d0a1247fe8e8f19a5760802e66fd89d069f3445ca88b11b1dd0f193264c89021438644fd10673a30bcf579a48b59abb9d5dcd3e8acf09a17c009d0c586105df142cc10e385ac792167887921be6a17035e880b2fc483ba9810d6f62cb7f245c1121c1de8344181c41430967bf255240e0a628250028eaa27602c67b56b01cf726fb158666c79161517aff96cd7d06bdeea629a1d401763110511238a0fe202e86241c250e27b7cb60bc8f7f4f4f4b8f7784bbe784e4ff63d623c7d4f4f121a2beea8630919d6c00006acc73590af2651e041039c2722da40c17a30f89e9ecd7b867a3c847679dee352da25c3d8e23dff01e679eeddf19eafdaf57acf03d0c53cd6e676732fc86fce6c7e73a02eb6b914afb945e1b5346048bd0003e38bd71c680c4cf32350616879cd91b40bc96b51af3900ba98363f38618e309e5ce84169ce08ce4c6186142678a9d2e382b76e9c78eb6017b3fe75b19e3913bc21e22770ef621358206e81b80784c80301e240edd21c0810206fc23c1038defa6c97056fdd4817b39aa6699a53d0c5342fd2c58010ad76f895ff74b1955dbd8f2f5cf8a208288660630c1d301f7b8011e70e36b668e1421d980f67b5abe87db8b74bbe0f076a97751fde925a603e7ca85d9afb7024edd2e668e17d388ff19adb20078c395359c4b1036ff19ab7dac55071d079cd8fe800d31c099197d71c83767116afc901e735cfa05d41af39912ea6d922e2e061460a5438c1a50d98754fbe245027cd19377bc471e20e9802deba67f1d681dad56fbd657dc83a51bb10f0d68db678eb19b46b7beb1a58efc0fa07edaa6fe558f1d68774314b25a48bf978f33fca88f33f9cd5c57e8c41c65f9fedeaf998b8030715e47021e209a43256dc61020b62dc9006d15f77a0766d7ebd25ef7524ed9a7f9d2885bf8e41bb78feba91061db4ebf5d73f68723d8476f5f8752923c4f9eb48edfaf1d7afc857ab071fca8861451e552cc16ed293165b446dc1218b292c60d7f5f77a7eaf8f98e834c65f8f63dad5f3d7e3937c1549c1460b165bdc602307d8f57846be929a90a34c1874409d89420bd84dc05f8f52edeabf7e3d4e9d00bb1eddb4cb875f8f6f6e194befa38bf7e141ba988f32b6bce640ed9aaf794bf321cd89da8564822ea661f13e38bc8f03e9623ed6732b5f146843052832eaa041872798e79e7c3d00052398dec82047135e609eb3da85e43d326678cf575dcc1bf23dce6f7c8fffe8623df60698b56ff478dbe6ed1856bcf59f76fd787bc65bf7d1c5acf69b5bf98a4c6b8ea8e20e26b22803b69901c51031aca1458e345a806dce6a1702de80f19bfbb8d7c52e15d4f3e811c3f3784f17e3c1638ae79e73cbb9c771cf71dce61c676786e77e3cb71931870e526730a9f902e3180ba92a35dc7883c61166609c05cf71be81765de7bc897c3114a739379fd3c384e77c9e633b35e8b086147544ad80058c73b1b1b4254c991bf09882710778ce231337c459e7e473ce4e6fdd5a3b59bcf555bb86de3a4f17b3ac20609ae69e1daf45bd96c799d7fca75d3cc66b78b479cdbb2ea6f9757bddfbf9ebd7b92e7601f01b1e3dfce6b78b6d5b17e358eabb3bbef8ceb52ed655f1d96761d040440e5a7c985ab3042c734fbe9270d80107266e48010713828065ce6ad7d067eef215955af2e5653ed42eeb9923216ae333c7a05ddd676e94b9069977d02e229ff9071b6897e6993709a15d9b672e65847631d41d547ce648edf23ef32bf29524441e5384c1e68e2a2f5680651e95e4ab6e31c10c34496cd1e169a95d157ce69129cad0aeeb99c72699790cd32ece338f62da153fb3e3399fede2a9e7dc7631ced6bf646851c6a9ca081e56700276dd932f8e2306a63228a8b1638c09b0ebac7619e0af18557f3deb62d785fc56278bdfbc76b1cdd2d7dcca57921065aca024c4162f4a5801d3dc93af21718a50d3020d69d84903a639ab5d42af9171e735a75d4cf3d92ea0b750757e78eb5376b1cce99bea3bcc122c7a3bf52b2a453a1ef5d0fb19e6a347ef31eda2cdd4ade861da353d7a8b69d704bb8b047ba9b2e0593df882e6d25cacb5d6dae122fbed764abe7aa9976c2f517dbb555a6ab74ced02fa76dbd46ec3b45ba776f17cbb7d6ab767da2d54bb26f8763bf5ed964dbbe6b7675d7491655996d9e1a2ecdb33376e323754df9e39654fedd999f60caa5d147c7bc6c5b7676cda75bf3d6b92af3867ce470fb330df9e29b5674bed195375c2895a6bad76b8a87e7b8582aa61a5faf65a6b2de3db2b9b760df976aa075f107de00aa2777f187db8f3ed94526a878ba8931375a2134ebfa2d28cd32f86b54f3afd9ab0f669878be6b7534aa9d21ddf4ed9b48bf5ed734abe2253649a91694a59c70e174926a709c3b74f36ed0af2ed124ebfa4c491724abeaed4917564779db6c345cdd4d42ec3b44ba776f97cbb944edf2ed9b4cbc7b7379c7e41804d9c6fef29f9fa40064577f361537d7b9c13ebb4cb0e17c56f6fa5f65e6a6fa67681dfde4ded1da6bd9ddaf57d7b3fb5f799f6866a57d0b73713dfde6cda658fc4f9f608a75f47886218dd7c7b9c922f223942fb152c5ca4d41e97da2353bbac1354bbf8dba3549a6f8f6cdaa57d7371befd49bfb80e8ade5421bbf9f60ee48b2583a277773babdddb5bedeabe7da89da8dda85d47be5d8376719409567c3b04da55bf5d3ee185bdf4d125125e18c9f0c208f5d1e91b2f8c4e1f9d8ee18591e9a3cb20bc90e37c74e9e4855ce7a3533dbc90d97c746ac70bd9cd47a7505e78e4a35328bc90e8a3cb39bc90fbe8520e2f647dd494be9f9e0190e4f1024c5cae6b780cb587d457f00ca547187f7b3267ae22658c515ce8449711641692f2ab6ed497bb24f2338f9ffc6ef611d9407ee6f233d0596883cf8261f45ac6b443ca61219ef352ca8f5d1269d4f2ab2011e9f62598e44a835cb7d89b06cf7f3ebbc7af264d338c21260e35b8f801c6cee29708d6a02186a9349660b20363777e592ada3883c69d35b0f802636ff14be8072a4ea8a14e0d78d081b133106b511a41c68b3edf10c28bce6ae219c67eb98af156cfab37ea9e29c68b3ee4396acb16cc12a0faf6eb437ac852431cd55240964930642c988ad7b6cb7197c5726fb58686e414d1e3a2e8ec377e3ecf0a6290033f5d4616aacedd06e04ffae3af74f3ce5d129e6f55252f7ac89a1ccb57e6ec1e17f53c7bf67932a89d3b20ec18afbf86597ebaa49cc745f2fbf13772f2e379223f002bc8fb9e853cbb0fbf581cd4ce6e845fd6dba3937c59700419d49e79d800ebe1ebc30a360f07b059ab799e65b1e42bf38f4e23b050cb3bbcd87e2be771d18f3a3b6a3ccc42354a973edb452518a99e0190a526d1b667dce837c6f8cd6eb17717e396f287355793d2f8611023ea94f423612125fd918bdf502ceb94f4b75888fd5ace78c2a427b281f621093e7a7f88e5a3cb2ef6133fa02ee6b37bba9c60d8fd7946cddf5f74fa9190e5dbdbe547427ca0f8b99c6178e1cfcfee876a5cddb8acfbb9f7c68d274c7a160bb1c7ef48b7d865176b79b77e64a7243eab5b3de36cefef087a2cc49e442365223ecfcbd387eb1691e7282e637e252486f6d9c6565ebb423a84f3b05dfaedcfe3a8c79ff9b1ba155d7ef1a39217ae3ec6e8fdf1335004a338119781ae94c2c60886524a7fe9310af317fa3c67525613bce8dc92eb272f7ac87d7b4be76e450986f1a547672e8a1f4a9752dec9493cbce81ff2e5cf762b3e7b04adfc2e4f29bd5f826195f718a83bd2136e75060ae3d34bbf318eed2f658cf3c1f36f0f23d3f7215731bdf410e4eb3948fa9a3959d46130948a9885c2effd6367c44263bac5d427e6458d9be1cd27e6454cb7d83a7b183fba51bbb59ed71eb23e64a88e2c799885622bbd0535e89674a32f836e49ef26e2d2b377114d44c8552f3d847649a761b027792143bdf4e94619b4cbba740d36bf9ba55403edc3cc43eb3423420186718e85a160255c2f81739ffedbb18b714744737e0f3a221ad80107b20c7aa3de3e221b4c2525faf3b9db202a29bde6f6eb670d64758be3675ffcfa85feec977e5142201f8812ae6f7f23473ffadc69602a2919f58d4436981f4a19fa26dd62af6095f95298ea3d38c06bd02d76a3ae2778d3afe4e827bf3b0d442525d875d979c05030faf5dfc9d1eff2df26b241382bf841b7d893fe4e291ce5ea5765be1456cd2c57bf70be07dd62bf6cb3acd2293b6a7c79c2a4d78085d8eb67ffb2a41d99f29010ef3c910de89310ef7cf5fe0a8655de8885d8bf9f5f38f4ec4fb639aadb13bdfdc62f668482223f44860861059900c8ea870f1faf87a7e3eea6d9acd2293bb2503b7724c43b0f8474a3ee96607ffc596252779721dd636899d943d64b0e62971e9f3b991463d2f589c5c045b7f8c3e8d52928bd9d3fae5bd3d9c3ea5efd567978d1ab87dc92e8512a68d44d8417723f3dfcf9e93fed9ace6a57f439e7bc94d6e6e817c3f9917efc32c63c46f8e9def34bf3ea965fd62b1846cf9cbfd07bfea6f7d19b6510bbfd580685f6994630ac4e3d8c9e81b35bed37cbc3ab1ebd9dbb25354a74ee56bb5153252f9cf1b3dd6a9f1fd2fce846dd49b4e31d4a27ad734e4aeb182bcb79ab95985abfd95afbfc38793db4a117b2f8e54d37bc8ebbfe356cf2d5b72ab399d52ccb6a0622d1af21a04337ca35385bc130e9abcfaf1b0cb95b2143b919f348cd81fc069985e6fc6abc93d6d6eaa77372bcd05a6fb5a2f4572c24bdc9f0e644c017366aac563fa51a9781423927068617ce8f7372d3a5876edbf0c23927c8c539ad340109933678b366b5cec9030c4b3f63d329e8758b7d82ab6eb127d9d83f5c069af7dbe3376decc6a0ba7de371549731357ad04ab00a5a1934db153f1b86c75164d87c68e4db3da900eaed967e56162062894952f618d5ef8c4778a194929a57fb69ddd23cb2d872d6630bec918bebf1ec20e0977bd02f231954ddbe69669fc2453f9c9d0a5f00c4d94960d7caadf0380abee0d959f245c4d923c0459eb34b205ee0c3d93b68978fb323805f4d6450f51fdf2670f6d6112eea9c5d882fe871f60ad8c5e3ecec6d3dc6568a61229492925212d6af563b6f7529a77df7564efbae7397b9515bee62f5fabdce1d77ebba51d72e76fd669cfda88ce4ab95c2c8a0ea2ccf95a0acf5a8be7aecae7397e48b4878d1edc7e2a2306adf8d31d3beebb73b213121ae5d4ed33a4e8b7f6fffad1cbd423efe2b5310c26d9fd472e9047e2bb76d6ed442bef8f77e52cbf5c1452af9924b4af2d555dc61e4aba538a83a15d512559552951287e930dc52d56f6bc95acb8fd6c29d92c894c417a12954ee7e528b579fa3989e3e4ececb9ad02e0f0c30643f7e0059c92536da103b7e689ce7f9f0a9aaaaaa52b3b1caf0eed5b8ee936b58a8a58238cb432f88cb170560105f73833c473149b5e58a7c1136e49353324806b17ceb48e038215fd64d20c28af8e5b88d9be0bb7ca9f0d8a86fc7dd4fa6d0e5cdbb51df8d13f2c5efbadb75178c663ceb423efeac2321be10bf2c83b89f1cd3ad1a5f3adde0c9290fa51327e4e39752b60ccf7ac8bdf5d5aa5bd6ad5bb0a1ba559db7acfbfa857cfcacafc8c7b0205f683f72ddd7ef7dfddbf7e36b29168a68bcb0a1a0d4dc8eb3137c9a740a235f72a9ab64507527252725272596e7c9a530d67aac0f3b420df9b6fc380a0e1be244be2d4b0e1be228388c48fd1b3d6e9e3003a5d427d9300164457f3839393db1f1e1333db92497e4926452c33d9fac2256509de7936c7a382bffc64e497f0776957cc9a90eaa5e9de3051c2b0865158b55f535943d2749fc353f9f9cea045427f2712720946fbefa904f72d141354ab151739f9cea56f59e0d94da27d974ab7aa4425e1f4efb240a1e47b5c0f41cc5e4f4fc990f17bb9806c6293cebb6a5ba553d8bc9352c543b29c542d5e3192f6ca9af1e4aa9afde52cd868be2575ff9e86224c43b7fdd7e2c2eba20104efba2111e47bd00e64329e7ab478f5d12cee337e72e098bf19bfb705ebbae5b4bdf76311b3f56b7aadfefa75bd5bae69a6b9ef9b59fe6578bf782a1087e03998542ebd9120df4b80a46917356ddf2d250df6dd45ebb368f1e27f8e89cec62d16edf181e910da25bb7ae79685f72b28badbad51eb6680ce340edca9c9b5aede24e498c91309594bc3cd2e6f1bbe320c86f60288267160aa3f679b72c0804b6aa3e6c7d189fb22f72d1ada96e753ce32c9fe493d7138c4fdd6a37d3adf628c66b0f574fdfeefde25eb5ff1ce1a2a8d45093cebd97d3348d73ed5e0e8c725dbb5aad60a77dd52ba8714701866de1729d07e49c8bcba0eb1de83288f38fbb95a593b6be732e038511ea65fd326a67d691747a4ec7e9d6ec2aab5910a9dfabfa49ab9dc043d65730e47e78f595fd6a1d0810b73ec1c71c64c1ea40bed5f7e3b39b6bd57ae54024faf3ba167dfe04a35c70d28ff43da71b18c5f3e99ac76e030f8c129dbe0646b0d5e8383f2d1826bd1ab1825a29df38bbaaab64d3e6a1f719f38b480621d15623f3ea0d275e40d56a745553b51a55757ae60da7a95a8dae924dad06a54d455b0d22f9ba2283a4777e893690e886442ffd7a5cc42f592ccf7bad490649d7a4e6d1ce6396b99739cd588b5d92cd33af3e7e9da577ce9de6990786ac1e30f4be6a3c600443fb9ae67cc57a078696032f18b2a4dfd9ae0a4a16cb56ebdd25d9bc7624c4cf34ab59cd43d65bad3f033d1692dba77da1cf4bb75f183f8c4d1f5b435dd56a40f5b40f2abe5cbff8733aad4b90c0f9f9b38bd1af560b90c82e5665178b6c81c72e4691b0f350bc805f3a92d8c524c3a91f7f7356bf3df429a8c17c0a66209f8246fd14c4203e058986e205fc1444d22da97988e4c3f6e9949d7aa8390543242f9d7649e2d75073493d3a1736c881511a0ce35fe718446a0d44ea495fba05f1820b46d19cf3a97173e82318c110a92d88154859ed9df30b634cc6abdd1beb17cedb6075a3e6beeb4692a1fe3ae7465d9dfd82dc2d8d0b2777bfccb52e769dfbaedfc8695f385f73aebb24996bd58dfa7ed372deeae36e452c5f5d7ee0a4d425120b6205d2a5d72f4cfa3aa773474112e44fe62dde342efbf856dee3e35b6561de9fa396a0fe4a8e87878787c7ab4f8fb15b323d9f39a75fbeccd12e966559f5cc48ce2ec6d3b35d3c204f9e737a77b1997dfccde6b85b729fe70be3f37cd0ad69d43c1f7f06723d6152e6757e00cf512b4c7d0a605ec873140a5bfe4a8ed69974e49c8e03c6aa6ecd0946383c753e523ff5ea210f58c124f12310af60d87e9df9b5f2d0f3e9d57bbe90e17ced71201f77508f275f3e5c76eee3ab8f39c8070c91e4f778f501d9070c7d3cb40d72eed3f9f5f9713b781e8824bf39ef3cf3ee9444a13ebfc128dca47e3dbc20f58cc7e96751787c7ae7b1db80078c429dfeece0fc74d92da14fb3aa9f6088d4df03d65a9dbb58e7dc6970454949e984ef36a3ce321a5b9f3916299f85f0d9e43b77eeecf1d5b32ff40d561f4af9cc8d7a9210bf8253569f8151a85be72e0975abe4d62fe480587218906e023e7ce5dc81b65b9967599679e65dd6f30101c46fa45f76571f7710f5fad96e5107f2711d443dfbc255cfc7e3abef87775ffd42af53629dfaeaf3e1f5eb3cfba809dfb904f26d21c35dfff1ad9ce7eb7123e9e3eb3eeee3bced5b75cb7e7eb17ce6f3038d24771bcca7f18c58c1f4e91975eadcd16eb16799493dede354e7b9a76ebef51ca5c2d35b6f6777db9cb3af3802d63330dcc07ae699c76e89f5cc3fc8fcba9134eaece376f090faaf672e379f9d12ea178c92f97449edd65e66af83f6ba74ea1c92ec4e89f528178cdd12eead7b9b26dd82a195a4cf40d0a97fd0ae2f9e112b9020f7d67309cecfffc2e32815aafe7e5481e9879ea396a85e06353c8e5a7afadb439ddbc5393bb3dff8f5dbb937d0a44a764aa46fceedea8c3febcebcbdbb58cbf075b10cbc43dda25e3f24dda22e3fa26e51f7f1b16410f506bd5bd47f7c401d44fd7295c3e3a815a03e01cf512b2cfd8d5c7ff72275e6ed46ad69dbb66ddb96f9b66d9b73c741b779abecf3fcc68fbefc9872dc656098f499cbef0be7c72ae3ec62d9e69ac74f82e1fce8da178ee0a3d7da2ea3cba5ded24ad1b568e72518566f8f5ffded0baf3c09f28e9d971f92407ef5b38f04d929a11fedb4ab7a4bef50ca23b59d6f0f5bc9a5797b04d39987b1498c0fef0c0c49b0418d1f8f748b7accc1ebafbefcc2125efae4b2afc7e3379f729acb2ff45e36962b3544fa391f46dfba0de46ffdc92f9c2f3df348152f68cf4096f20b635524417e187f33eaad5ba28161ffe61de17c88d479f4dbcfe321527f7b86d411ce47e7d9af1f9e398f67a0ece883078c227d6b8fdaeb47267df8e6b2fbc2f89b94d2fb0be367d1e517a96205fc3d5f78e4a97b5f88e4293f7711fca05b9de4cadd9c8b91eb62e4620746b1be79e4c0fac645e7787adcdbabfbf8f1710775ae7ddc2dcd2ff755bff7e3f7be2131cff3d1b7609a5adf9e8d40da8f7e0cf3edb1a9ebb7b7f8e5e32d8513f3cdede021c98f5efdc717c174506cea56bb8fafc541edda083ea2eff9e2d3f37cf5efc74e6286bedd7e61096ffd963033f7bed6f77cb1e979bef9dd1746a66fd7be3076f9f6edebba25dc473024c17bf59b71dcd77efbcd67703e03dbef10ee074f0af79bf5cc4329dc6b9e8121bff9f621dc0f1e92fc0d8c62bd7ae621927ccdf9e5e399731781ea5af68548f22dd87e7bcae60949f0d663b7017dfbd5e319f102feea5e17ab703ebbc1fd0ac6336205edf3311095495c4f50c49569ac1c237baa966e8f7ebbbb47d2f1a23733737fa17dd99167fc06c3f099853829c65e7339f6482e5ef61cb585ceb7f730173117d5678f9d929a871955743c734b7631ce072ffa6d2e72117d76d9713e7871769c0f5ef43bb9c845f2d969c7f9e0452eea67af1de78317fd562e661d05780e23dcbca1060b45a43903c61c14fdda69655b6b6db4962db535084f88e5dbdb85db23fde3bf930b2f3e43b941f3dc2dae5bfd24c83d945e82b65b71c943c10b198e988217f215cf5ec60b8f3c7b103141783e62b067784ef6396acb0a1f5a29bdbb985c59166a094e1fea73d4962d1f2f68a56fef3c3e9c3ebb25dd7e510e2f7efc683d59c38f387e442a186a8bcf739416aa14a25f373e5eb788785ac47c740834b3f4610f7f3e3a9137dfde41bba2f3e7758b880493f44be728d2a8797608b4ab3ffa4fbf08f004d46a57f423fdfa018b8e640f153e3a1114b0e846633e7a06edf2f97802988fee41bf222cbafc625ea8be580c4a5f2c862df2b5850c0a0e23c3557c8c7981c3ddc5bcc0e916ff8db3394e1e8e538ed4c3934c784ca5092a76dc89295809302101f6e2d92b93f1ec538d676f67af3ebd9da2f1a263f1fca3bb7c0971107b06860978f60a862d0a860b78f609861e183618b2acc78aee1ffa478f6ee5cb7a74e7d71037e4c4d08616304e50c0a2b7f8e56d3953859ba522b8100316c5853637cc41869d37506c408e1b4e6bf830060d3460d1b5cf8b5b78f1a91c4f9ee185f185e7aca91baf25472268c10211027873aa02cf8793053ccb441047c61254f621c7f5f06b88b3201ccbe3566bbc15573fa0d6cae3584e34c4ad7eb2363e0482aa2d570812f9e5f10f7e19b177d02f66a2765567c7a05fdafdd0f3d43c23d1a73e3d4ee9d195e8bccf3394d219cf9d924985b79202f499673fa20f836e45df80379fa394a6907cf421ef3e4729717101cf32017ada17c0abeff58ba158f56b752bbaf61de9a0e8f463ad3cf9fae9a0e811e8a33738a53c0d8c3c56bf1816ddfb3561d1ed198f7ffe8fe7a8139cf8fb1cc5858a0f5b3f73b80c147698977ee7a8a232b24ec572e3493194b7582a36531cd68783a2cb2fe8a395a397a292e7b94bbfd2ca8966e2525c5a8a4b6fadc7245f7c27275df07a8ec752c000e7c306f372ea432bbdc1b42b6ca6972ebd99bac9b64b1f7267b13cafa387431fda97d143a20f6d9444532fa7f7cfe81d464b43f5d26f84c16b7719bdc170fe6ca6ce607fb68bf8d97b09af7ee8d567d52ddbad66aa373caf59453c20cfce61f7ec74d258d927f30a72f49b3ec3fcf4d8c566bbe247e7ce7c748e517ef159be220745d7ac8f1066a17e066ad90414f2d433333333f31732941c4a7041e30c1fba58610d0c50c951040f5cde904385eeeeee6edee30539bca859dac2881538ca8d295c7822d3c41c14ae7d8e6ebd90c59ded1a3cff70e8a30ff1eb09172de8b480c6cb154eb0e844fc4ab2c1e90d2919cabc8006132cba11bf246066061e4c3073471c3060d135e017cb51c208385c8012630e3660d1796aca711928bc2d84273dfa16af9b74ab37d0ad2621def90f26d0b00825833a5ec0a3f4eccd45f1d93de9138b814eb7acf53c16cb7d7ad87a76962f769e4e78e14f3b774a7cbcc742ed33c6f6e122752b82e1853ce723b3774b29bd25c82cd4b35b11b48d86d73e8df0a2b7b327c5189fe61cb72c4bc49ace73a4a88818ca23d55bd64ff7b7d1e9ed6b4a6ffbe9a3c734f8825ee215d678f60fd83b902fa11a5260618b18799c210563d740beacfca0859d1eb678a2010c18bb113b917c2599598a430b145be4b1c21b18fb907cb19a1e96e8220a8e295e7803c6de72961deaec914ebbee3315cf6ee5cb8a19289c79420d1d3227c0f889244c689386196dd2bc81b147aa764d21fa28d3abb347385c349f3dbe69d77496ce3cc5b34729f91a12420633de8439014c5cc55574586bc245f1656b206dbf92c0989186862e77d81025074cb6f1a28e1fca2051450e60c0a4b3240c6b5e4a3094c2555f86b4ad81cc212bf43430b4606859dd6afe70f55dc1d05230f42608932e77486af7603688850c831eb44b108b742ced49485ca0047c8112b027bd3d761c23ca0006f40f99ceb7cf1c3c19be1d094bdc12bb442b83daade7b158ee76ba740962913e414bc403ead32788653a0593be9015b9254f74a3b6f265e41135518744df5df5ed61eb43fb2d5f0cf358fea11d226a309ebf6df7da59fce2286240620a366578e1851958d3d0c60c18e4b820c7863ab0f6293f2b83d8a34c8a4d9b8ce48b082cba06f24584412213984efd02ed122c015802321e486f90083bc3a897a07d82441824213e3bc3a6338c3ffb793228fafce21dcf3ff4242e2facb104154f5257a4a9a0cc983253684144174d30a9307adcb1470f5f307da1f4830f6c1861c60929e0c0a2cb8edd49dcf8a40990778b8584bc6521f6a983c71e02711a4744f190c69c3c8250c2009c3769968640410534435c7cdcdbef2e66bbc55dcccacec6d005e8bae10db56b56cb7d2192eef945e9f3e3905039c1fe420c3efa8cce455f45ff89128736ee57b817f9a24c17217620c20c19079c31c50a68f0900595155060b0d1250f33b884610406ca78634517519421440f6c628c1c46137558f0e2851f9a9e92764843c78d99378088e3041b442863049c2374b882cb0a10c0a20e2bead040c752135674bb7f7737cbafb81fe7ade14dff154b217ae9517a76e7ce9d3a3f41203237ea18104b0ecbc02c4430321424c2c1fc1946a4f8d28dfa935e821ce578a334c40b518401878933a8e258a3852cc66c91030e20def4b09d6b780e8bc848830eda3d6857fdf60fe47cfb06fa95c1da9f7c48e26720120cd87a5feaf8aef36a58fb1190a85b11c9c72024187004c31f9b0104da15329491060cf5ed530c145eee9891c50d67b8c09a0e2f61cc9922ea05396c602dc677140c69bebfbdd53ed4df6dd400788e820187b7cf5130ccb0830b60aeb8e2852e2e748953451571ba2e96049df7bafdaef6f1472d5a0b615ef2e0b5ba55edc742114d4b0655ef3e20a73d7477d635cd4651e1f4996b31ce076f7b8ea2e286d7dc6e9f6997fbb6b43a019c77df96258771e0910eaa8e82c3b81310224912dffb690f1eab5b1aa56f3e6c00436d5d6477ebd8b3b349d82abb49bb98f51b3b6bafe4b2db797731cd3320676d5feb7e9dc74e091094673d74ee7e9d679fe635a37176d7effd32c9c58bcf69b2987ace335816547010fffdf839b0c5402e35cdde56e56e0b4bcf9f65f6665091bb6c3eac20c67bc3dccca9674e37ba755b2edb7cb2bc95b5d6daed2392b90523121e7bc6a5d52d9aa43ffb80ba45dd96e1651e729fd1cc33b7208b7ae62c449df5c345f169b6f1f6c5243cf655bb58f10e8b8b36304c7a0ddcd07c58c153674dcbb26cfb66b768e6d6ab53b75edd6675a862b139f556dbe6600bd2a5c67426bdcdf2d6936c20110eaa6f5e7df3126479ead537d77cdbb68d3fcbd1af7e48c1ac044f9db74dc3f216a4d917a52ea974e885a9afcef746a7e874efbd9ff5cbf57ae8f37c3d14f27cb5ebecb1e3053ef77e21eb7aec629bdf6df3213812c142d559f2159f3aa84656646ddd29899fc66bf1b67dfc3cdce5eec7d3ddede3ff200d134dbfc5a614b4a6f0410858dfe31960570f09f215c2075ce43111133191cf73d40b643cc7bf96b33c5f7c9f2f06c11554f7be38040b55eff962089d57d6775ce583ab3aaef20176318d67fdbae5f1cb97ce39d32cb187cf731a26b670f1293ac531d1a909cfc7dde7149d98aac9136efbb806c142353ac5a5afd5e352171e1eee8ef93062a169f18b1fa7b8f6c8c8b28fdbc1f3bc396917679c136479d5e99c3e27c8e643235d12219f79ec3810229b8946699f524a29b5d33367975ca439fb74768d8b3867dfb8a82be271768e8b7a9cbde3e1221f67efe1221fceeef970d1cad97d701190a2099c7dc545419c1d0817b19c7d022e12e2ec41b86888137176215cf4e3ec43b8a888b313e1220a9cfd878b8c387b112ef2cfd98d7011e84ce9b55fcbd0adeed22dd95ba49d4987c6a970b237960b8dcda6e6a2e1cc746378b8fa45315cfd22986ec92843b7ea17bbc42dddc2d22da46e49e9d693205cfd36c0e2eae78110ae7e1018c2d52f03225cfd30f8e1ea87a40857bf231470f503ea96040047bf680457bf1fa0edeb18ea570589f59b5741f2be7a15ea48f6ad1118beae99c0af57a04005ef7a69e9a57f5f9f50e4b96b138c74ade45fc4e3e70e0aba58a7ce4b37f2453988c451a48b73e6bc740abed8c610367eba5855f5d28b7cf10c216610e92215d54bfff962182c308674d18d74225fd422481642ba3835e48b554cb086d54529a9972ee48b69804405e92214eb8b4cac9698a08b67cebcf4205f34e2471140baf834c11785f011c4aa8b4e4e2f1dc81779f0d9e14717c3485f7dd106ef8b8f2e36fdf8620c3d30f87491c9c71757e051c1ebe292cf174fe89ebb68424f1795bc6f04dc7357024f77a5e7ab729f3b1258488ec0f385db730785eb42e8be0e68cf9d1316924db84f0356487e20fd7e14c89ebb09685d07dbf7802a24359082d1e7ee59481a31549102584812d1cf02f9dc1d807643da47807eee8266d7ca3e1000f1b9fb58483a0b499fdf1016927e81b8da05cc87adc34be7b7425848ba125ee8bdf4565c1405603b5a4a6de69156192b659581c609d62a66599c737e99cf312fb32ccb6259e6b2a3ced4631247ba7524cbb22c034329434fbd15c70b811ee8480b687e441d051a669dbb09449805876410144612e8c88fcf39e79c73d2966e24b33361ea5ca963b7d8922fcdadb373644d102910c0b3f3c41b95b42f2ac9a0aef322a2eec50984bd40c32c7865e8eb570257d03e3f1270ab630d2ccf40a298c2b74790c8c617dc605112bfdf7843dd2ae1cad0f71012cae6c3a84386a5842b75c2d4b659c43a146b0b6fbe59f4fb61591aa5ac9f379f3cebd4c3a1a19e5d75fb1dc9ba24fd15046a45cf3c047a4b67c781101a723fc95c060a235364aa26f2d531a3b62c3956bca03afb90e8b23ef40a2b781ec0b39c022e9ad1b32fca4b9bc40adaedd781d86a9f9f0758a89d255f1fe0a0f6e63b2c0f3d39df2c3be8c7bfea624f78610cd9a3d252bbfc1b663d2227b16b72260419d4bef11c89023ffd3ad034b0890c6a5fc20b89be89da25bd1d83159a0fe30b198b8b5d8c997429c657f9a45b9dc56c661e7f66953a69c242ed5491296b4bc710109fd6c35a9f5a6d12f033b7ed406ea7acf76e9d122146ad751c08d143b68eb42a1dcaacb80c7467bbee75b7d67a8c69edbaeee3ecd7e7dbe2b98f77a779d4be9884cfc730fb01754b73ef6b7502aa5f19954479ac6e9df1f92637b9dbc5ae8c9a7b1fc3b48eb3b7d334cdb9e3ae8b697e2fe7f36d21d3e33e1e3bed6a3c3e1fc3ec27b978fd1c25069adfdc3a77313437fbe2cbce82d18aebfcb3b360fcc193cf697430f3d757ed82e339bf9916c9f57c2dfbf1f8fd3af7f95070989608239379bc1be7f331ecda8fc7bb53e2ddaacef3014579978dfa05395faf53aad1eb16d43a0b4633def5bb71f605371de7831841bcf5ab71917b8e128388b77e37deb66ddb62c6138ee037b0d5ad2e49bf7f40d73530e4fe7ae8d7efedfa813a67a3b69ccfc730fbc2d4f37b1fc3ee9749c981b25b92691ac88a7d2c549df5d5593f9d0b763eac80daf8f1ecc1e232736c82b9d81e6314628e950e245f9b47183b3f21e3c1e6da067a32a86eae75734b9ed002f1cb9c7ef16ff685d0ad18110d70aeb907d7b7eb13f480730dc442663a0712d1ae8a079a5f104b4b06b56b1460d8900c6aa71d079ae621d1f736e5f6314cfb1856bfcc9ff2702c5f0d0ba15bcdd16f3aed36009f7accfcf2d6350fa5495242ea213ce95687d4a3cbacd22fbefda244a73e3f9b394f9434ae3c86b66ad0d1a9f5ee36d032bf1cd75d04b8a10fe10d2f64383f1423109dfa1329ede21e9e0f36e086f33aa09f07d1a79c01e0a9601869f4b8a3272cd4ce70e6647d67581579edaa94b3911b838bf8a35b6bb36a3d1aa3b2d6ea941e89ce45fc4f2965f5a55ff54ba9b5195dd1326efb8d17b4c642b1d9a31c73516c90eb56c72c0bb5471fbcd0bb4b32dfbbd59188d0e3d0506b43b45332dfeb567b24c2e397beb25cfc348f6034a201df2ebd855ef3d83d9dfaa52e350d0c45f036faac323ff4bea5f49b711c2fe067e7eef3335ef4d0198a4e4d10c7f82aa9c7971d07419e82a108be8252764ba64b5a27903cc7b03d2d67a90c178864fdb07e82c93309ebf1d4ef9c5d4c7a9eac403ca1089e82928b5b9b67ec19a3f4e972fa7439794e660b784211fce480a21b97bdf959f9711105ea0c74697420ef212494d6cf28838c7ef188a854bd36791202ede8966e4925f9f58b4a5fdcd24152039c5f100b19eb1c686590a400c3220784c7b00dc6b09b7df1ef87858cf52c1bec7afdc2a8bd05c30e8ce4fd187663b7c4e7a77b1f2d977599cf2f4cdac0c74fafb55352806b37bb558f7c9b678271a95b720b9729b5cbabb4e3b6afced12e56593fb4b2ba0d32bf3df4a5c72d587e7449fa91be2b6578ec21f7ec2b8fab9f45c36befc0830fe4ebbaf45b3ff9f7bbd241d2e987e4837fc40b5a699501b92ffd4ec0d5cf86e1b5c7a8c4451bfdf88974d4ef956e21754b7efdf78b4b5d546221e999166f7b2cdd927ea55b51082f643a2f1d4bbba2d057e95778ba585db5eb470308b0541a1b1db26d736c43e20c31434811220c88d2f8a1e3470e2214705ebba8f374315abfd6916ec95b359f3eab6b9e7d71ac068692822ec957e9985da45b523dbb9ce42ec71372e033bf5cbbee62273bfaf184493e7c361d5c31f98c51629f517e367e1c6db9845b2263d269bc73e310d6edf49e2a29fdc2ea51c09597446334e3eafef83767bfdbd79241d2efac5b9c6062f32bb9edbb5e33c0b00b9660738619f5ad6462fcee62edb76397c400af79c7a380962c80f4f8e46d9ea43af5edc3323dcb06db6eec38a014e0ce836d826107afd5cb53b72fece0ad536f157adaf5a695d423487d386ed704f29e2e46c6e364e74378fc0c6b8f1e7a7ca61ebdd28f55bf9ffb83db3e22ed494a4a4a4a0cbbb2baec92b01406f8eab3db60ab9ecd4ec9500672dd8ae09065ba918c375d7a1ce34d8f5609f5ea961a75045749338ea008af7a07718c9769105a9f7e69c73373cdb71a3dcb324a35225634989ef903d80a088c7c0646b1d5efb55f785dcaf6d66fd671902961358ef8c20602acc6115fac78eb37f3cd8d64b6647b0b6ea05da2b9ede78e9227b4808def36ab5f7c06a260144f89cb40a1f7f15ba90022ed3664929b5ffccb53761d489af3250379b09e4d9e9f7356ea752b5eca655f0fd6338f0508598d154c8f24bf2e01c336f3d3b3af872433d8ccc1c50da9301964dedd03927e7ad639c9206b24be8251aa5ba920088c3021b17452b7cce1763c69bb3abf7ceff5ee62b7a3d7adf58cc806b68c17e5fa1ce345b11e4ecfb80befe6f2c3a225de8191e93ccb0693be0aafd78e034a812f30e9dc7d20848d1a477c61a3c6114db0c9453263bceb5c94ebf35e376a1e100c3b78eebae6f1b52fccb21510dde2a4e146ff91deaa5b1d7a51cacf72934aaf6ea75d450a560eec92971e81f0e292097ae04ac9d04b50080bb5b3bee08f5c647b4e5ba98773ce59c5e843509f8408f5118a8fcedd03928e84618c1f639c9fe5a6530fed0674ced85dcc8feb96e4b853123dab1e521a6305ad1b49fb714bb208ae3efe2b39cfa7377f2c3da9b190b42c2483f0dad9b6aba987f1c6ee94c4be334e963ca1059e52e73c376a5ba387b51bb461bfe50baa9010ef7ce648d6bbe34789558c9e04231f825a2549e94968c047ffb8720692934d74f2a25b4ee3abf85bbfddf19c4edd6b17bb74297d7631d9ecd4997a55329dc66c25e7ec96509f6edb7aa8addac5811676f0b6a95b2d33c087d91b5e74ee94b0120348f1591a9f4de725b22712773a03855efb8c946795fdb3ad53cffc7297845948f3cde3373febdbc72cf48549dfecb14b326713e1807a9e9ba8d468d7caa3819a4361c7149aa111840105e3154030301c148bc5238aa2c9e20714800f7dc04852389348e4c128865110c518450801c600020021c020c3d40cd50638129bf846b091b59164cdf55bd7816e409633528a668b19ca8cca173a1e6d70d235ad3edef93ded8f34bfed0b94f52d7d919f1f32800e7d010649d6e8aeba1a91673b73cbca5835577c2befa92d16cc1c9cb3f4ff92108b9fdf0555e29725d1c6ddd9dcc20b6e220deb64fb940b0c94dfa7c244d29179e76c77eef8db7926bea2635be423334dd30ff1a807adcf3fd47964d269e8412cd87769523c84f538df36026f58d4ad134bf50ccf3ff0bed97a37cb9d8e03085105db019b0652616eb21fcae09be7dd779e5e339facbc7ba62c3735f71d7aca29671cc192e3923fb2133d2a923aa00b660d7130812006385f9a7dbf43aabc032de29e3cf843a0298353b301c22c7f44b8e51ddb502d7afd73fcb7fa1f62cef7e88deaec6e7b1f1e17190078ed9583fc211044f5c258aa065d82982e88530c5c935325882e50f0d0b960a7c4dde6145494cbab47b108f3d9869e6c6c3cd7a0094063d5f1fa54b5d9d6c87648a93f7dc6db9a84fbf580b55f6b29fe7ebb7bd8df0b5a28d4eb95ad6e2c738be38d3a00751248d6ecad434621071560b250673308999bb1614b116cd1300c8682c962d93e42fbf415bfd774de8722d2d29e846c95c418ee1e188be7522f9ee6ab9c696111d47d8906a763b88faa8c1a145ceb7d861167fc38b20dac9cea12ab9c73c0e08f94a62636272081195c1e12eb0dfe96aab227c601ce3efc0f56be57a6f7dfb3778ea8bd9d33ff72902aeff5da2b8b55f4142d27d3407ac4582a602af4bb9d5180faa474ed46e8db823ea1ea978f737c9ad7cc75a423f28e66cda69345f6a9a74107ac710d9be57a25d1a141cc5078c9cc46282d4d4707884b0c75a16b03de4a8323c224ed358dbe229754e61b20a394f40cd84c8e6e8615e70244ff9bbbc57a1914cfaeaab7c08a1334538c95830b4f5e620d6bcfd9e86be3dfa04bda10de6a8a8958d4ec232779775fb7da656273e985946419c9fb2952ad57069ce9c4cadeb4a31d6e95827669647455d5abf7da1b258d467bcbb4f48444b17e1bfdc0afd71f9292d3ebda7baa3169741de7a741d25f08c1cc3faccfdf8acb484c06d26331a90dccacce505a3a73e2f673e6e536c53e1ee65d269a698919c3a0cd8b7a9a8e3476a827d6f7057dd37bd761b143b3e770661fd7ebed402705eb9e6cdf30bee445a9a44364d4fb58a1d3dea8cfcf4e101ee6e42d3103ffc97ca076c4c57336245f9123e63316c866397715acacc007242095b567187f13c3554002cd7ec838b87e4100e27e12b892225a5ac0d5570b685027b2e361c3056be42a65e398bc79e576e45375e4d837f2205f0ef512f8ea3751d87a1703fad8cf5befb237f2603e1cf50afefa53f8a44bc12904dabac51bd42548ef8d1ef7c2bef98f2a4121ce45d2d00a0665178f3159bd95775e710d8ba08c6191b78a0abd49716277d1a1acdbc4a6332eeab51f5bb581a74302a364e7e511867d1198510a0f4d62491e1e50d9bf42d761b4bceea0867e1ada594b977e20899d8043d1558ffb423631e82b969837982173debaf8bad5ff4bbcffaff9235f0f3571dd840bb0d050566757504263713fd5a504aee79eef417d15ca47c4d2b73b65a656845a99c3e3e713b1d4a8fd097105a0c486b84c00a189fb63e6bc6948b1f2615b34e250bd8a686d94ba2b89b4fd786c84b60da3998ecd5e5243766fa29665e14993eb2c8b7f78d44ea5b551838903110bdbc6ffac364bf4d7a802d97e05dc2885ff538124997c92c9af587e9838fa3005a8c869a47063cd165ce0b5577d6b34f470d9d7f54f9f7df5c3d2f2f862569a0085faea8df8111f324fee4e11f56b47044286fb443bdc7dc2b81ff07e5fe943f71b6dea6a9282e201990ef58102656714dcb0cc96e5f0a4967911d29de6213b5f99107abf5a421b7215bc2ec8ccf74262c06c892c6fd5c73c9fb28f69df15f23e2c6f9ccdcb64666780362013db02a23def7233ac1ee8b441ca258ae4b9401a14b937dcb3aed25360435bc3d5f3ba2f6e9e26dd84af8db064f90161394ceac04ad62f885deb412035169301d133eec46dfa44daa218b4740d2d50ee2839062dee18d20f592b4a42628271867997b49c5a246b812ddcb6da91f8220ff581348cdce9e47e9cfb97b7394cd8ffa3b46f4896b5e89a59074026452975dd170a0eb3b3522f7ec4b58d09e28944ed57f88eef2d2d38851afdf827e073c4da49347249683cd26beef12f50c0a5a416a1645813bf2eb618fb970084f1666cb91c1bfc9ce9c3f4b4cb6ad808f4ef27dd78694897d65a850368dd747a2a994fa961786379b50b31e11885ffbe029a7cb9b75980fc94df2eaf4e6f0c0e906842679e90eb67d16aa9ec8a39790dae8338b495526fc17f320736a265824bf5dc84818b8279015092217e3767c5a6ea00e6a80d965860821df01fd57227cc44e8e83be8b0a988cdc06aecfef8e3e41a58063309b959557c52613927ac43bb94b323542846eb352edaf11478b474ab9c114a4995066f428073d8824018211088bb8cfc6477a1c1c0d7cae3c07336ebebbc8975c2dd879a100265a506d4a45974c2b538052dd1263ab20c56eb2053658a59ebce168ef4ac3b947dd9520636606d6446fd5794885225251ab6c0d31dcbd1e0232fca38d93fd42896c37f75cd9af07df6f3ca46c4659cc4391a37cfba6e3f8fdc65f9f14ada8ee685f4806659d8f28f4589bcc2010e5f208dee9fc322c509c356a6c8d28e09f98fc014fd984a24edf46ad6767a838b3b454a7759c6c90467c5303080dbc24352c55c13bade270265fd603928843766331441f04f6a4d22a993366619c58bdb37035a18cb3f66a1d9146611653ae1f8f1d886718e113e72cc7e1cf38fc231d07963582c5f393856881db3bd9efa3da338571fd3d044de17f798f6b3a573e18fbdce386420946121f7c05367d526027ac53d8c506e1439a5d502c830f93f16d462e7a077416c32c156f422fa0c998cbb0599cad52b3726e7cdcebcf82b99039e155e229c6eeb98de5cb79318f07da7d331ae65458aba2803a09219879751c130ec2dfc8e29ba266e129d3aefff9a3db3f86f95cf03cb63905467a14e08c71a50319fe3a425d0c4f41e59bef20e8cb42d18c1083fc5443e2d994fb3effa284b8f6e6cc87746ff511a3146d94660792037d6e071cf17c4f4d9613c22df5620857f9a74a723132add563aed56c4143ea02f42b923ac7565591c80e518a8df85dbbb0ec7320ac64b2c1c600a86bc9a3f81e671d98e0b8dcc2de22c8eac33a9abf1677acdab4fcf54e22ecddbe95143a0a305405dd0456ef600fc895c94b48befab973018e32bb3948612636c588b899b882134dae693aa783034d2ef3ea8efac58ffba823db0682fdd19d9f2f2e6c0b99dac0a9916a2ef476b8efb70bbed1db0d9e91c8942ce393591b91e9e5e0251472ee0855b201e28fe2368ffd7fecfee3635bd627a710def947dacc9175017d023e19390d8af342b5fc2c3937181fcf8e42608d350396db23da07ee46465300a756fb42587c1cfb1bc2d703a6d52da9a33549db6c55d19f6bd8ee863f0bde5fe099c5a4f0c06aee2cd7900e074b240a135e34c9d2074bfc515be0f791d7ecac837a1e6760c5ce2f02298fb7e353bc1d9ccaace6e05e9be5a00a1a0dc59c3f1fd0f0b90ef15c3fbd29bf8e1928f48ff78f57ad6d17c272f4ef0d20d6edc068e1fe8027d5d6ffbd3064d8f934882f8667b2e806eed07cdf9fee34b8338dd826143fd50a0791ceaf8f15819d99fe91e39ad2a8d9e0bcb9f6e3dab3917c638350329ff99e319736bbce66316f7b840cbef2241b5d6d7748f8e2b6d136c8a6880b5c2f353ec26c3e01a3550d46a6cda1073bb37bfe072a83a99e112e8cd0d36ec9daaf7f60458eaae87f61aff455ce1d3ca5d69be5d6b409eb1fd034ae14194fe6802e938cc5556c6e17d507bdfd0dc582aadcf3691c961968593727cf5c5c8875761e5f395111a25bc9267bfb82a1c71241ec29fe99bac35bf7f4f85ba5c28c001d1b93b8d6d5942a8c1d18daa40ad904eb78dd426d50b5a54bf5e2a961a54ab55936ab90a5713eb85a41082a2be9906642c28c0239bcba1afa289565b217a7eca9533563bdaaff0ec66a84034e2041db0c81462dc71690bf8cee2ac04ecac58ee8371f8d953502b778bf6642e620a592a50e491c16a3f57a692061f67599c4b33109daeae529ee7f8fcd6cdddd3797814f0d980881cb38566ff2ffff37025a1fb6855ea031301ee3a5542f9a11846bbd814c74e22e8c9efedbe5f00c5964a09113181f5190b48ce3b2690d422e45111ae05d7bd81fcdcf86d0bf3c70adf940af4c633fe08a5685b148e14eca41ac58f3f194e8b840b3920efd9823ea1d37f7f2f1f85c34353f6593f04d1088c7372addaa68c5cccb17855d0af8bd027d1405355fe481a23ebbc57efce8404cf74c93464d95b6c21b3c955bb4e8ac864216d199074619005db42e7d14eaeebc3507050d2848bffcf098a6bc2dbcd81fb40f121e28602648898d6f5f1e5ac18dc7798566ac4f0cfeffd734df08b2095108ecf3ca8b77f11c94600b731604639d1616631dca4a52e3894a3f8a1dee6d51781f70b35141c1c82cbd2d0fc869cc10c60f5cd72b1502ca7ee3910b8a0c1617ba71e9d04e7776e61e668dc1814918942326d4587771e541ecc15c38db7e0290a6f89ac0e3b60ad46e22ab4bbe55b1c3653b7b9429ef9ae5015d274582318e0f0412ab125eb32d73de2dc2bf40752a6174cea369f62c87c9bd7c89564d060e8ed19e35cf4c96bd7e8a6705a2bb08330d229a11c8522eca270d89031d17484540a1723ec042afcd6380c1c691a067fc2d8c95bf7f5e09ce6aeffb114b4a3df3f824383fddce6b5244cf09d49bd321d212171b63d29d6948b98d924969789d54a0954b4151075f53edaacb86431494182c55562c713b71af935acc893688558f827af037c015a98f67b219763ac9b003653bc0e10e08157e48821cc1cce08a0c58993421e9211fb52ef8379603b7c4223926a6ca50b871e4eac4053ea0575f5828c31d647d7850b36b6ff2dc16689a6286b90fbc3022da39ef2205cec5a2f87f08527a0036c0de2783e44c80894274b2fe0e29bcf0c2e64c9b388d4c97f37062c2a81c8120288543f5358cbb27551750105e5faf07893d10db6a2e10399d21ae81dd65c85051173e3008508220d7b58491578e3ea214e076a8ec9039d3c098df670460e51a6693853a9f293e811af6178cfeab37cf90bbf5d3f34ed3805af4fd49f0dd82beeadfc56bb0cb1d711cc36aa89e06bea40367c2335f67d9c847f99f332eaa8a3242adcaafe823cdb7a0a87359beda1d1c3ad794cdeb3127d995998fa35cb91d896c172effb70bfb3ff028c29775e948b80cb89e7d5b89ed07e0c4973e769018314a6da209622fbb8411add8945175453e8005b5206a899051af0b644e7b90d4a40d8839b7d2578b29fbe28768eb89eeb83cf64633d714e9f00271f435b38c1095272457f28ca87a217230c3f4f94b8f53afd65ed3f949a5f108477826069a8e1b084f673ecc54e20d828a1e611a08cd594599e20caa5f2f631cb60fc95e122a1b94f2ade624c4a248e8e760ebc1a193d8f36328e24c904508436e7250426cb741f1bbcc2252cfc8ce7d70db8bc237803b48958b0e5c42214eecedf244754ea564e68808d003621b56237ae94d1dd5604d7c682905eb66bdfe006cdb2fe9bb8ebd97c232313c8ddd7c9c3cafea0280873e3b107d3c169fe5f8210d2613c872cac44d4abd595b1ab2ffcc9b23a39d7803758e3700b60be17e3513a33c2836063b6449b30ad3a4f8c971edec482ac63cd90c05dc1163af2f8ffeaac533812018621bf25a30652762c1f009b34b8b8126eb1bd5102b3c59e93a49c98255b11ca355ec13e90dcf9ff0a6b4a36f903daecab70920fc3a56be6d80fa6c6627b4d8263b1f2a31e929d1d64ca5f5afdd8125a0fa3fe77c249f10d9b9275161e748f9845924292e6d26ef7b5461e73a28fe8fd3ada7c163241968c56d1ef7dbdee025fb8fd4ca83c1747ca9c6334826d3f6f9bce906d8f38ea560ae1a7b50c85ff976c9d1450f30838209e82232c4aa24882beec6bb45ee593107a8623c4390e448f5d0542f2b466956daef179d34990c1afeded30eada3bacbba3298f597062b759073496495615ecba65edf06dba7586fb4454045ca59e26cef44030d58e92a0fec712b3d2dc78d8705c459b73e8d791959b91b83536fdc0af1b1b9ce8101d5efd96024fe9cabb58e12f0a01167fc8685d2cba09e92be39961f6083d0d9e5310a5e0947d0e644bee722d3038fa7c52556f2d28ff45796d4d26e86a147f89439cc669bef0dc1172e03dddad8c1e039695be94fe8cc2e71fb95d1dc161c5c7db89cb455043ad14b3b106237b79e11ce2161ca663980b5045e39e15e15e21a779e1054950c6b1a4e90c94b4782a51ec367bf8362586f2e60b125f15d222bf20792cb620e41974286151a3a6b4b94e85d57123ee34d398d57b10e04763ddbdbb4716f5189a2f31528587008fbfac6e3a6ee59d33198eec3975ba8abc71531a807b63d2435c04d0c7c6d1d247ccde10cb926241f4058da9388784c29b42321d2dadeb7a612839f9898c09650c612da3579feeb5fbdbdfcc904a9fbb7bb7a56c45793281bdbef5488fee335ab51a54ab22c7e8b2f4d136e6c0732321919aa87c39b08e7053d8183583e49aaf2aafd9c666fadaadfd12bd670c252034c3b082a04c0f2e0b4159dffc393e6eb56031257e310110e93bf43ba8a81792cd5784fee81ef244fbb72e17eea08300c5b41e87bd9a477b8e34f5a9696203fb9dc8704754b86d29825283884383708f7d70780c7a760f1438d9e5c24d1c7f941625b6b900f784db6eb3d72908d63c7b25bb0735a697318031395e9f29d61d8b37e24d6d20247fc0033731b79542e16c3ca24248182b56e2e4db50c7586b50a84e6efa66bf18ff8bbab20f9ec8ec5f99969b43d7dc8f9efbd83beca633172bba8a8c2a980b46b87a56aeaf354c56f36b413eac1311717fc6d4f26c47829829b6904c832750981df69a40404206e0b57a669c77753485b64a999e3559716cf242a7ff016a57cd3eaf74c56f245581708ab4c2c91a80d3c9344e09679de707e6a362a73b9964d24658dc63195382368f573740c745e072946368156c315ff57f02a07dc3c29ce753f5bce36996f0a7db4244a5ad33eca5cb8da76230cb5496be469f230262002b6ea1a0eefc7083f78a7487806c26cee25818e9759655cfec44d806e99999be41fe55ec26e440d7d1ed393a67c65fde2b3b07490cf5295373eefe64af1d1c84c9560060b05da7cb0511df0a4a5e33cd45f12e929f6abcad7635418b39cf292ebe80c4e616acd4c4c50a872375a5417c4de59a11593e1de1cb94e3aea79b3c08ddc1b0f98c4df4633c96905696d8d74a9ce0308f3520aacb0e717e7dcfd594952a7da4f76bbed58d36aa34f934aa2bdf3a9fff39dbf40dea400d2d6a89df8fe6e8696d03315f3ae4c687ba9e45b92f01b5c36f80dc48bad3f246dbfb574a2d19b76d9f309628aa40538783300789136b459e98e4d5668e661d35027b233cd738f882919b426a8a3879993f9db589603f0259ce5c95e525b57dda322580986daab56da2551130d628621d19724cfa3020015df57ba8da43feab2b08c2416d8b07bd06f38d76c716251470cd69a2238b624e7c5b78c8c3c1f3eaa2650aa92a28eec2369567146585c831af5524d54ca9320ac9531206405c7c6fbf1b74345f767f9e9aa926a77f543048696780ba1aaea45fa94416b8ef781d8ecd9d40e6d8209f3c6000edb524797d03349eaa7786d80172000e1ca00d28aa220c35d310e63400e3903a1d5d53713653ba74ddf49fd054210a0545ed95372e7e7bf22e22701901bda43c29394cc325f8f280185686fb372e6454bd2dfc9340d6b738b98d9c73686d0740154b73040d65da7dd5fbe0b829362d806a5a319931573741951c1c88237616248102d4a02b5c0ae4241aa19cfa7094d149f0f1f0c219088181f2a55eb51cacca18dd813d3307a14d4f2eb43a64141f9551dc311460e9581eb24755bceb307c6044284efed1776068be3a5ecfc4004f9233a4f9ad5ae9bdfcccfd1437e9a077ea3f4dd4de75e16f0bb6e7a10c61df022c8b92bd65a0ac9ac0eefe38c47eb2c0d6f8955bc0cd17d50bbccafd18568b1bea0758e82b4448c5fcac95e43a41b96a0132775f36124b429c2367409646ef634cfc4df1c27697dc853dad9a1b1facb11baaa93da50bc825a5997f6f3616550a7d89ca8c111f99897b90605bebb7ce906dd26a133b360fdc82f2e3d84559d61bd10a741ced851da7e3a7b889f6978cda1e69311e67748af7339ec945aeabc28e51c584d10149bb3e84e06d587ddd1220a178b57cbb26717dbf3a6856222deb9d6b9dbfb5a808ebf23445b4b11a29a83d828a405396b599d4db96053415962b7be1efcef63cf132c87dd2d0f24ca4cb01709117be352ab8c6f0ee1ab61ba0fb6f99567bfae001b7cf41f563010239c794047cf426510dd1ae87875a0a10912eab18c4fd23ababb9da6543085b36b14d1640341cbf269c7b4dfd93590e09a9c81f779dbdc161a8d6ad9cfc629de584f0d8ac371be9bf899bfb2548427c8a2fa8a3fa2b1b655b688ed4ad38e193603ff48fc2beb37e9d39916411709c2da206f2e85ce2743b546ffa9339656ac065fa0fd266759cde577cd564923b5fd9a8533c7819c234e31b4ae0bd6ab3cf0fd63f48beda23efb0ece41eebbfec1ec39509235b625619643402b1c58420090d3642274bec21a86429bb74e066530e395488242f42bc882e880dd400ab3fab395ab4942df3b0eee1b79ea5af5ee8435f9198865ad2828895744929c225854e1221edaae0c96a72648d51791f89af6080262c6c4e83830f813142c00080e087aa67910764a2f470a4a4007e7562acaf50ecc283e34ad751b13b3939f0ca41b30491a1140f91d46c631ec7a161e5ad27b0579d24e7f30157885aab8ca6a354331e43e13759956c6f08d296cc348217ad584e770aa4a55219d2b914675542eb966a1c67893795a7745ba33673f74d3aba39b27c94ffab774a98068cb08a096b54546785b8046d4d0941d4a7ca9aa6273132186cf5a7e0d157b0ff98a26f5c27ba051934bd48c4dcc56360704693b2ff9f2c0bfc536d90a25697ce4ad5e19bddfff6bf4cf8e0bb40bbf5df58d27c2ce5ff3536707270a234828bbe48440445246936c3b85f07d3559b9f19014f1bed3427352c5a1be073262bbc22534922e44509ba0c06bb63a792f0145eb7fceb68b09698c8276d074e2dcc50b9ca49700a6a7b6a78135c947db160a9946fb93a97830d23cc1a67fc8a75da26fe45d26b266ed98066cc454dcab90c07c74cf336022fcb0672475974ae54ba345c8a44800d1dbd3aacfc2a93e4aac3420e15527b0a3919789194641072555192d85011b91562967a617d928408cb83d03f2b5905ab26792b423a4241ee09487906ce527d5d2d72b1560801a81c4acc02f56d63145c8136e9dcd55798785e53590f809ac49e50f7ff0e17500398a5d6347f96c119ad816c6f377205c2a1a4ffe108bfd6f73a8a2524c943a9420c1bd10f1afbeb0247a7a61415ca80a6cc413acd89223065c080ac5b53f1389cdf895d9aa38b0cf62cb39f5d0b4b5ee2d5cdf09c4253b35cebb202e04c8d5f328c32cb67b38f5a81daa33a95ac2f0bcff768305c4761957a08355f308c53ba8136c2e09902ca79c968716b09b06b2054aadad4069015c4a4e2a81859a1124618a8b21f9f3ad3ecf1bc8f00d9cd422e57e46c1725076b7653ecba7fee5b0c40b80c310115de0d4f5479a943de9ce2fd89fcaafe21dee5f3fbe26e3b3053371484c78aeb1392a8a7a8a0f06977cc79c533121fedfb2e593fbcd46dffb626e7bd2ed2ab66501e825bd75dd1a666f8f60248e751db2f9648dbf66e06e8367ad37b4fe3cb9c09f5b6378f5a1e8a0e8f25d11aa43b2a2e7a0a980b2813eff8fbc922fc83270a1e32d45a09ea051cc54f4ce2cd780295cdbb27c4c86a54f229a821e3a904cd7ca9a287aec5994a04833a2738b3a7f126e131db70661c08197386da99213e749727445b71b737623ac01ca10f7a96e350956afafc9366f36f314247b438568e4b6aa3a2ae5ffe1ea0bff6c3aed53e8f70f49c6b89862b58dcf4c7f804ffe551e50b71b0a8778389ef034c83886a15e3b0ea9643b2e3cd80b778f07e6087ab740854b44ced8367c9ad3b8f0617f8272477b8cea61423a325878230643bb49b870f1d7675e8e64110f6de79b0c2a5386241f2ceef4b6f783c2e58e511a6a1454f4b2fd375d7d20d4f9cf9b012c5d0c4b8dd2721ad6d883db5a9eff0d91257963c757c8b8ae75c044ba5988bc7d1ea2b989bcf56feeeafed72ab06acc0cb1c6c39a17d9bb33ed06a8da6b5a401710bd7fc23c9ef5c142e7c1c26fcf71f4f2cba0c942fcda52243be7cc0009ce8dfcfce507925dff1e0c385653643def049d2e41486eef4a4e7927c82a648f0f832ff1c44f8bf198865600739f4a74907bd9ee7f746919eea8ce808547b55913cf420bacaeea4840ee31e4477ce70ec172c933fa796623cc224fc0333f0eca48c2e088a502ed404144880320ac8c5fc9406bb5b498f0e03fd89af9d457d6b60d66f388f8ef13043cc0be02fa851a647c803dc813e056def60279a672150d68e8e5b2e089d77570f6da56962dbe65007d891bd1314abca011ccf411058007c40b4369dc6d204c2a3d9f0e33bf6c649c995789fa67ff4eee9640740daed9ec86f64bb67cc8d9d3d320c53193f8e525d0cccba572dea90484260ae322006fdfbfdec9387b49f9206fe6f4bc599e70fb576e34241b1b0b3cbd66c561edf4bce1ced0315fc86095f979075e0400695a0051f581585507f316d690a9b2ce605ea9e1719ff999b8f10dcf5ce9419fe087f6940de04822fb1bce78c029ec6c362fb76e16ee8fd79843e0607928b2dd18f81f5554e4ca374f5838d74420775657aa9b1afc8a0a43eeb93d32f23709222fe95da5f0c7c6ce16c9bde5171baf567b7c162e0b4606c251f76b50f1859da0748be0f2915c38fa0269a0e9d497fc49bf509047749416c3b1eb83df939112048782fc10fba651b5c2faf4103cd135e82271ca5ad26e1556570a56e4a57c6dde0416d7c6e4ac498fde12939e147c4fb6c3266d3dfd64bcbda8f5fd25c864d26842ebcfa02397763e58bebd76d1ef4ac31648f2ac08ee626f35cd2d904368e161678e808ceb5e491c6c65b0087f2dc33e3362d0672c3d2477347df3beb96749853435a46116351611f936d0b323be547048aa45acddba48750aad3ef3e85f5904ef96839983103f82810fc6a53c74e606af37a31a185e18704cfc9e34c8039b5870b519e0b57b31778001790aaa8a5ba19aabac240ba9b1130ffe8e23b44b9e0d5036d6530a75b5b67e69812553755d121ef90044fe941966e8d6ed2a3940b70952b6054274f5094d04036684a1a5ba4fefa08d34c53826dc79f186a15ccf9dbe2b4da6a8d7d1c7643662da0ff480d47a128f1c56f44f69be84a73455167d2ecfeb4250be4a3a411c80142ff38105e72acf1b6c374f4a0b0737e8457f5cd6c6efd617f5593756b6588a43d8079b24021e79f4dad6af51157dad170a2d1cb431b1f18a18147d460317e3caae9800a69340cecc7a99c07eb5118d1484ca4b9be0dadf4cf938ef92708bc2cc2acec41c497211177be427b4fdf54e838db80cb9a890d5b24f191bc6f43406263038d9abdf32486cc81974de297b21b121768f14e41c60bc31d0f75fb50f4cb724b50044d4392684a7a244a3bf41805c9df3b5a63b1297d9caa240a0ed762e66055b6bc6dc0e651aceddf567abf4c9167b2b888d161a3035e401efbd6fb22155fa432989938bfbd1c68687de810d8b57b3a701fd36bcb133b21641b3a5ea372523c0b7314368cfb71040903e26f8017e1a3c20283abf579790b776d56b05e543c1894ad48286edc8dff795857d9254283c27e465075873aa27a6f454d3b130eb53987a090a5ab188e91a759f85a9b7c821451a2c6f043c9d27d6a3ec76f743a5efbe9dbf0d7022556a2726d6918edda93d2e8274cd9141256b6eb8e291f29fe569b52206544a4524111b43cc1ef7cefe8dfe9862bb24ff39b635a754681613d266c3b218edb9ba5f8d74ef3b29ddabdd85050f0cf97cd559a1468f1aa87a988b7d2e08dd66f357169d372087c0eb0a6b86199b0c55287970640ce127c43e380137d6d72738165d5998221447ec803792f3e3e32a4f6e80a068d0bbfbf87a735a379c36f9dfc4d8af4cd24d0d1fa364364c90acb3f64fe25662f7926dce9005b1fb33705835ad9cf5db5b271704989e20ef0ff85bc61cde16c3a67948b1fd651c6e38e3fe2c397630d96a1a972f4af4d54e8df867ec132165af6d1a3bccf19e5aa8d68c47a33b0d051016839502b2256882383785b84ffc498d66e9c006933104b08c3ad32a3f6b0ca05db172787f89cdac930cd923e14263368b4c2e29e91bcc96476b178e833a89aa60aa1c2d49a83c8c3a64d7545b48405808229271248fa95c645f47768fb8ed5cbcb33c141b5b7ed93e54f8fad867319006f8c36965b201fc1d0a7ac75fa1cab979ebff5e4c1c213f8c60d52ad1d39d55377182e24b34fb4a01a3d072497abadea73eaac7ec3a80c1e034bc8c292c57cd3749e3c790250603d144237337c33abd41df15068c64886fe9832dbd6fcb004baeb9ce70e4767f670375a2ea6411333a8661b8363caa74a0a742d29cdccf742102ae0b16e481877a6183ad2d429e911355c517a27ab9aa2f27ea1ff955e63454ab01a186b76804211392737307f144544522e829a28aaed0df80c0614d22f93b4258502d121bef27c1df7f5f26184703a738c7c44d2bca336cf230608961dc025267b5f4b4a3d121cc1dc9ee8e542b182cd1165c237b14f61f5b3cb39f66879fd482e5037c9a4191f30458b5f6235b8fe29eca7f9488f5b31f27dfdb2e9e1b6e8a62b97561c051056b703b7c2ccbe60402f99c74aa30838c5d0e6253a5744f97b45555f7c10bff86e52891c038403378ed98fd1fbb3c599fd7ad997373a2fd0fc86fe3001314c88b9233abfca23103d023a151c9465ba199a65ef1e18f601844205349076f12fd70180590092fb13d98bd0ca948fd8fe151e1ef52ac0990c8f17fcd8e000fe0dba7d81d533d87e8f3442b757ccd038302148cf483f030d0cf8aad4c6b8f3e0ecc8826b219ef7a56ed9401401cfb2f6b52edc0e615f9a3475db59b24ef1380b2aea6a21348515ebce65abdf807677f1f2cbc0ac239f99e5296a9284d0ab55ada680064800845924ecdad8a9f2171b7ae2d866ef1bc523dc9b6e8609abc8e16381f4968302a7365d19ab60cbb3009ccaeeaa0dd815f50c197133d3a06d35b45601a6e022a563b2206701969ddf6f2d605b9ea40e0c363b23c86c8d1529b7a4d7e807390ea78709b243a495965058763113ebc493bc27a3ffa4717de9c27173f4226119d3f17c31c8387f1922cb911a0d171336efd6d38bdbda3a77347f2bbc407c2ad9a679b64fd5f23c8088694069ea204b6d35c29e529df7b0a0f8e562241a2459014b50e4673dc72a096a8b9cbd4df8fcead0c84197dfc22df2cc6f1442646162d75c6e1ebc907be3edf12c9214d9f7f7bbfac1598b2829b4634a8456415a94f2becb1965e83fa83b885776809a0dca6cc7abded0cf018a943b5a1e4408fc6ca197025d673ddd28e25f8053561af713b4cc76d9c6ad58008bad70f3ce3aade67d6dff0f21ac95ae5b0d2b710f6194337e51a1c63c19a19263b6900512510c46f9d77fe5dbd8024ad65a4601c00a068f0ddd4d91030923a93748308d3302caa2677d4d0e7e26d3d518a0e989b9976628146f43e1f0b56ee1aee745a950aced30316e119c7f64af6336b1eb1b16cc17d34c3a310d65989ee009bfbef2a30c1d2f2f77847dae51cc13f129606bb6631476183143305e902c143f9ed89dd7eef7472e56e894c69d2952e2157a5ae18e909186dd91b2eea7aeaf24117df1187db6682f0a054ed409873cc35a494b675546fb1930e3b91164eddd560baee060147d048339f4301fa3abace0fea5e6839573c513f16f9364bde4c425f349bd486a4ac0f7c874b5664733527c0f82b7f69321932886ac046e5a99fab0c8bd5a2997a90e00050494a967eec2470b668a99027690aa2fe4e139a6c1f6b44fd51a6d0608fb831717857d125bc5ff918f906d0e3243f6c17d48f48f667778dfa08c3089bb87004c4657ff6def5b647594141cf66576a97c65967b86925079f2a1dad2112e8514be092ae6c452420b707ad65c05d7a1217d6d0009292d5712bca216023ca30ee9ed2fd358f148c1d9aab444c261a93d7d0d70f664f62bf4e09c4a14e5cde8951cd83c82214ff26bad693cdae51b45bc3f6694163e0817ea867e005937db5a2cac5887a83cfbd4f443da2d829f5e13df353d121dded38983d0088d75e9b7eb45645e6a50f6222f91f6a4f685911790698f581529f4d066948f5acb302162c6a01d41a9d11cbe0b37f0e1d746c6318e03df7c09cef29a48567e4102e9a0b3457126816d466214a16a5abfc355c9bca5066dd9b4f1bccdfaca2088cfdd56d6c01bee455170f13170e1c2d31209e6b6c6aa72c22a6de594b51ce3df1e1aaf2962ea2d75089ba5817f2a8eb874dee115c83317a7e13d57ea98b0e3a91ec0fb937d93ac4a3a36aa0a39b0a90ffb5095d81904b8f969e49253098eec130c9450b24d26c6b00d62bfbf7c6f8c4bf63d65e83ad066d32accc21e5bcddd6e5b9d0d9500ea3f510972e5856824702e7ddc150e310b63e93b617e517d240139bb68e1736f71f4fca4557ef88b32b23b84dc299b43b1f7823fc5d85687f3847d90ff5a0bb48db1fe461324e48c5b8d95693533757b25b7fec1720bcdd5fc89dd99c9e92de8fe4662adaa90eead5385abf99366808857565f10b05132aa3b094c0cce9c17a326d31be0305c3cb690de4c2a0a84b6f66568b0cbe8e04f95851456758b345ea45cdbf66440108154781063ef85b335231d71fbdb7bb25d64fabd985c577b6f8cfc586761f748c042c0ebde70b27e3aa423a8931c137207b21015ec00c9a3650008cdbd6445bc254448d95e12af63f6eb08894568bd11d01c8319104627d9944dcca06532437bc1d430a179e3797cc60774fbec55d63119956b6c69941a0a3e511877ca8264a209bd07af41e2e6fcfb734e0200130249932d7fbce54d8feb3b4a36a3a7155e64ced021b25c5d6385444d6bdd26d8fe6471c49f14e0a24b5a07bb2d63b4bfca68d0805fb60a8c63ea6746a1bc94691b51ba5a36410467d56e082c8891cb0f1d161c8da5ee3f398832648f7260dc14d486ea17216b90af5666c5117e98b36e32e40e4332ad5a06167e01359d1aabc4f926840ff6266d8b11d12a2809bfe643e004263f4faf73f52bb53a1b7094fe51ced18002e568fe0668bc960c0a9adb373564e6687b4fcbd6228863140e7a8672b6a6e57c0a284125f851d6cf2b0d86b5a9525433e85155940d0e6d3339d5ca8090cc4f385d1dd90f3902ab9e57c0f7a374681f61700b8b5e70e09c41d11a6367c71196de9988a0f8ab8d6542e6686c1e4211ea28d4dc2479b4fa39b0315f42f78647824ed9845ca9c3c2277770e97bf295499387584981ddd9dc29073a34a6b3d66f1093e52e7ec496b4e9e10eea14835df2723a71a2b4cade8a8ba5a3dc7e4c8e9d7f6d624b3a8ab2de7513b94ea882cd8a9d00a1d64f7ba6ee11e5b9a58f8a6c7b6b0af7bedaa2f700445dbd1fb5471943a6a98e638f18d8ce193ffec7d7e725a45e5d435b90f8da053fbd8ba640cd90556597d7d55af464aa15cd94dfc8b9baf8f634db942d80be9fedb48d9ec2ce05c573401e1221f4b5dcb3a2a4fe59f54fc8e74ca3c09dc29426d0a0fe8167719499f632a449d8e3283d71aecc5ff31ca8003813927bff6ac1dbe6811fdf4a467e231dd4ba385943f10a1d5855922f89212fd54f0e87af71e5651fe720cc2c2ac2993fb177de209f31defb63b77e31c3a9e7cf106579e04acebfa2dc595710d6d3cb958e37dc4ae5d62f7604aaab1c0a090aa4bda004115641c05dbb5b2b40db6745c7ea9e9809a1027e23e32bf67e10e520dadadb26629dfdac7a4331b395c44bb95cea3e7f40c076dd0de47104ff6c8c6440dd28b49eb730b7677f7f93045635daec75dc5791fffc410490e3571983325384c37e73d4d24359dbd1052d22b35d615bdd8a55db1c31f535de5d210566992df6518f322d0bf4fc340e5e570ad6f6240c14321c21d83760191e067699a412f7df4096458eef87fd6bc57eb12872c1dae93db28823fe46c0a88d80faf2cfd3a09d400ff785b9839aa21d8534cc9852e9c4e1f83800c4103d24e5bc0eb0140480c0bfe451731e23529fc5f8ba14e62f0ef72bf89a54c531c9839afb7cb3044a7ac30cbe2ce6c1b043ddf5336d3806da44dc946a91bf9774e4683676b8ffbc705612c657c4d0945ca8b8735b30ff5332f39c535ee8d4a17bf72564a2249ba81b73fbfc8d2be22e859604b79b9665312bad73ba6a03e9091907e406f7dd7798076b75995d488026dfad781c06e712820643605c09164edef84e10eef6b4bd51e8f24cdefccf09533313600e75dd177517d274c899d71eb89d78d941738adbb17d486b39434cada8c10d3e62f3338960fd3824476a7d442a29f6f551d5bfe24be815471eaba870b35f7c57cc5deb665e08c18e573ed8a629dc3779ff9acab573de96ca178a7fbc442381bd003f46e8a4602f699e59b02a4fce5ee87d56d4be771242ffdf7ef96aa517fdeb69eb3db2a0ba2403d5722c704fc2181fc85d52029fd343b2373c61aaa3873921c021cb7ed2f112ecae9610689558a9ab468b13c8004f9d1f2494017ffdca80abf1225f3532ce022777fa55864342ee16e9b12472fa29563724ef6a989ae99112b8d7b95aaa5dbfc8d28de487e0c1b6dfefbc476b6949d6bb0193557ed820b13a681fbe4afcd441da6f61d971c973eb6ced55b81145dea4fa63e7e22605f5ce9ca6c2d4b6b1bab005b031d7ab406096009f583987fb7fc6b5a7f6d6df8985fc3469a6db47f9ae0b26f9395af960b06619adb46b9f4001d601524e5b7210a0037de9a45cf08fcd958bdbab2557b54e0e1a0ab8322cdaa3a93856d0814b16597c244be100965941880977000f456499e431648e217b9ebda9c079844ec31a88d2d7dde64da05ecd58fb90f1cd49204e91870faa9939cb84ef09cb23e51ba3a15c61a80b24284b4220191d37a3d3ae4be9895aa6f5d3aaaab6e9a8e593b56c070b6174ec606e5779562d306b705b2435ec194c1a68960c1b34345459978b5443606881a66a186bc12cc8b69837922ae16aee4126811d3f587d86f4b8c787bca73a352a7539e151f2a3fd23d6d19af3786c8b643b567db574cecc0018597746c2b2c173901569402c43ef629363f7332b90ca63c0a6d5b326ee206a8ec9720e039ba9f1ffbb4a8674f56b3062191cc2d52d7ff189f8d329e428fd5178d324ea559545fe77272c414a6b61f880b38d90b1f6d421d066c3643f3e5d9617920f9a910a797ef912fc82ce44172261fa6c7732ac6af59871e431178c9919ad7d6030a12ea3118085a2a329a6bff803bf5b2a63fe33649d3b45c36c1bcf88c839f053480c7eb7c2ee4b095b1cd0d0fd0faea3075eab2d9d70684f11c65ca85a79058a76c5358de6fa62f788ba02926a0fb44b5901c0f71fb583579f39593c0a91e3214d3d2e30318f87fc84cac3a10537fd3ba17fac68f2457bf7bbdbd60a6ae89a2a8c53d76b49bebf2ee51ae103417b06ef0ca1f8134db3df3025c55fe0a0af4a7231743a41fdfcd51ba45be78566bc9f53b557f87c0a199e84e7d7fe0f733d8f2445ee51c95882f69c331d6444bd0afa4d0ee5201aff7d2a0e333f30b8647141c90e9181d54d2959bdf92002dea6113f9532abb7d20d2d4f53dd3904c4747cb9a908d878cace3af653cc70da68d6f0a7231fb1436c90166f66904445604c51bc6fdeab92b5131cd951ddba4d0cdb510411f0dd91bf23aabb516cbb582ff42cf07fe523e570acdfff3c045a77951469c4ffdfe0f5ededf0e0961315b5df8871b90c794e5b031b70aae8f3c6447a496e96cb41da83ad3602a3701214caf4ccbb0d938b4264fa014bd52314093d2636f425f31fc56f83b12fbe8e990422d8301a6b57f4e4d64400eeb4b62619348ffb08795567774efd866ee099838b87e80979c3379bb045f6b5f9d3b8a1ee3bcf6a73716971d374a2a142ed0b4c5a7b8b4adbce8cad52d7250f0e88e198cfbaf3fb7cd39560ee9447e7b54871107737c52a9ea0efdc6d93685a8d86fde9f30ec404560502d0afb483de994b0397fcfdf55a04231b3dbee5a71b8fcee6916e56d70c32b43a10eedd2d3cea8f300951c6dda4dd82f3a58aba5e3785a9011ea1af25bf8e137bf3099820651b5a53572bf1789fea9439b0482d96f53b9d2504cbd36257a7d6fe6ab8e16b7cba020c1dca6686b421689d0688d85e9f0f75df6a03789539a17d9c4855756fa1cba40cfc112f410e8e6d495b1ed88aa79284c2e567608940f1aa15ca8420065a114e576fe7c3b34a8733c2e9b8d7ab0cda5ca731fb5bd1de525e3029179b86ca8796b39b285e48e713965d3e24fc57c6c0380c42c3ac406d669ad0b8948a82ddcad726b11786069c23eb5c5a6f16d57c6e0f5e361481826c0e9b67338b555292cee06c3d0ee4746aa4a56835a0b643d06b9194d7eadc6161f233e8cffc06a8131ea7f02124e8dca2652061cce51a0c06e72889cce6880cd9c33569f3429d902f91295b0a9960aebdd0fcbf2a342e329f7832fe904184922e86eab118f725f44cc4d55b679d656b60d9fd814727fcc06feb56d031c331a6840ff75f769c1f0126962062f099aba6bd506ff58e5a5c31a0bde5811c049b1ecf70312ba56177193020fdf4ae4c0b038be0c7f4b22f8bdbbdcab1f963b7e719e0ebfe77e94775ff753abcf38471d6bdde210b56cf3a33c8b237f40a3aedb316aa699cf81f9ec23d3e91a8d79e4540212db720d2b89c492152e790dcbce0a6e49f46133a514706eaa325b0f223e035ded4baeb08fa20066ac6a9aa5c44a0c35743257421720b72becd8873b489ef8bc3509673b17543a30ab8ed8930d7ed80d7f577161ce4db8a6ae5ec5fc380f45a862ec9ba7ec114c5e560c66571e127502211c0a6893ae3313936bc1eb6be52f88170bc9b1d1f937982b16ba9e5e3f36b8a436b722786d156b9f6d8e0a4958c3e1c71126d65c04f0170a8a7f19a04810c1e75c7403fb9a05aae0e1bd08f0ed4009eb4a78ae80be67bff3f3498c3c1d312b7ab339f08c0f76169e3315656d041e661cd98209ca50b8bcd16421ec4d46e7f03c476cd2a757b751987bf07f71f154ee1a336eb1e853c6422e26095a3665226343f59996bafb2d01a4f96c95dd164a14116c14cd626a92c3e8e200dda7137263675585ea74e7126d95f25e90e07141d3b495395f4f29b503f12c4c1ea53a7bd53cad8a80a9cae831f8fa59b06f1f03c3856aa93343b51dd7207d5aeb00cb8254d5cf95b408195694188c1b4edd5aeac49b10d62dac787039d1626b6153642535287aee831c480a4b889e790557b9a113b28b3725c0aef6467bd34e3ebb3614f1de133db519a0c0872a32f48c3519f5d7129a52bd958848b84a4ead9073017ef94e2a1e175df39a741fabcd036339c0891c6c4f4b0fe521136efc8a92a2d2cf5f6d4ea91580158c5304cf76d8dc82587635426500580a78214c44d43c1591c5fc3032d3539ec6b05ac4396e9a3fe8bf0db1c23f35207c628cfd83e0c4c7afa4b984c16073abfcaaf2580d00272b295aa4b11ad15c8c5223de0a2576a58b0e19625fa0a0f3781bcc9dcb2820899df8413aebd8bc801c2bdfb31b0415cb35f36ea2595ceccecdd3d0191a56e1b305e49936372c9ff11900742dfbe503cfefa0daca92a6d05cb8ff36cf94231c22fb7883b8c6e1c666d70fc63dd3d219889bf8290a02720b82aa7352c4068d39a9f87d167bca2f4b6448eb809688dc994194afcdce2af4020a2eeb9d7a3317b9da5db2b784adec2433ab43fcf92a5129914b07d9e70b0e5a89d0805e055f44eaf837121e6a9aef28a7f76c34d83e4116cc9e22a688329287d89018a46f5e7a60d7d6d31de652a4ec3e565ff338af7ae51b54db9dc6ccb769549c593518727f2f8f4b73d6bf9db0c6ef49793656ff9b441440a94ae2fdd0084c0a72fa524c2608d887f02c610ac9430cb0c2a1c5c1f551b0ff3dc47394210e3e8bf24c2b69c4e733d2a01e66253a6e7d164eb717866cd86d1492c0a4efb18ca64a4c0613f7e3818a283108137d01fa8613da7ccd8917b57806793276387362f7c87156e8174f05062e6abc042aa9167b7bdb44706aefa0cb43e76ad47bffc2217108cecab2b4a96f4373236526dc1606143454133d537e48e489b904cbc4e2623519eef7a258547f847ae6b39ff0126a5cf447483dda9b441e245c5d4a773dd3ca782ad51ac3933fa7e27f1479af8583e2c1480aadc5a5aae7f752bbb115e0de2a4da3c8b7cff43d47e6a0fff125efe1ca40dd9d61b1775eabca43e0de244cf30665fa040d8af73ff061851b33d883cbfdc23f6d07700719fd3fcd4d37a8b3ed9dcd6b11ce420e3eebce96a99b7d97dc005ee62f3f0d17b233d6cfb557b809799c95e6c269e655e6bd6c7e6207195bdb5a2faa8f3f533bfed4206763f94760392708a483a3c556b8b498537f1dfb8e1695df90c52accce2aeffc7ee7ebc55f6594b79329770b342d38e627ae6ca5c34b9265d0d8dfeb9a97cf9e3993e242d1f3f2cfedc5e54567fa5a3757f7f5b4c4e8c42d966a7920f4f24cacd2946365c561aed74ab7d61eed451f66c22636ee0022c34061829b51364d5be70977cb83d9c676dc521982b108c3eb00459c277147fa924d0f31984c46052279a8e226be05e580b8482efb78dba4cbf44224d2d040f3a9af8778ba7a0bdba4690353003ed014d8715774c4b39ede306b3091b75bc69f4ad4be7945b09614452bb13f7273164a5c6ea5fa1e951af14ac338aa8d3e24a038a442a9ff24b04e7fd77c179ac70229619694e447567afbc51c6adb8b8701c31cf9b8fed58090642e82ecbb2cfa2d614e5755d921efcbb5c989cea924eb4235195541f83861a51369eca700e5a19fca6f4193f8b5d974d3b0a2193528101238dbf6b9c53c9be60379c4e2c6d6287928cb449da7f79e9ff1c7f30bd92901e310b36130a075c968b9a08f27014ddd2b19bcf94b3b2e9b79770a865ce822044b2d2e1670c05f8de2ab1d8b59bbc8cb04631d1e0f7d9680429a110ecb3e98c53cde65b5f4a4806122e2103a3c9e048bb9c86405c35fa7856907a16f9b2849c92a57fe0ea45a488ab2b358b128cc070c58f31a07d8c8c8d17c05982be10f7795a744622864e88dbe0a5ed0f9e07f30325701b49c2aba271bdbc592db2df4188073fa47e55302ea1a07dc6c687cfb990998dd21b9fd28f1382c979599daa914f0811ffa3fd2df7e0ba0cf491b19c5e684d6e6d9a8b37e5ddf872391d62330ac61a80c14a3c3a23866ce607875ed3651bcf89e78f9cd8582abac1fad93aea765c7e9c0cd217ea3e90f66770f4562382affc382d82afb5db26ba829161ef0405b110f5a075d5f794c5930a05492350c290ab1200d83c221fa87b5c1a9f44ea416219b3feac686fd84a61f0d0002bee609d7d238b157a0e1c07592066841baac41a59e956d6d525130d2b9ddfe74105451b0ffffb3fb2d9ee8f98013392021d5d565602c53267f5d9521b8c69ea376d17edac204a10b6cfab70d714ed00e98ffa51504ea1759f62e518ebd47dd60038926f360b0500fb248cae66da5c3e259ba1da7c02f4a40c343d44fd0fc5d0b78bbca72abedcd0615beb0d4972333221aa6f9becdc676b1cb167ff694e60163a6034bc145f57e0d5f53fd58924962567f21a8c19e009fec5cfa07f8804b10978f1c20f063acc86435dc8fc15b5d8e13838e8bea1cf0ef6f57c2f398a95324bc87fa66591b63818807bc24a04ff7655687947806bdcba53d7739c735425ff441495c15503078db471d59bd147666790024856c4c3aef67a3fb412b2760fbdb0680ddd30e84a98de68724eb40d2820b69fa3d08e4f314280d6b426adb5ccd0d6c8fa159290c282df19abab1dae88a929c40e9aa6259d2770a312b5c3ca6f1186a4fc0784aae13512fad1828591665ea6d362aa444485a15f2666512ee6b2c9e7aaca77ba235b4c43f80a04ab9f44f686ac18b3fed9e1bd0bc46e029427438240d30854beb760096571023301aeb0735a7660c59fb901470d1ac1d57d19a095016f9b1ef4d4621fa0df80670514684f1577130db22b416dfa364efd27f65531870482ba3bf5ef2e362e7ba9f6df4e06cc60768e7f085236b01899e53bf7a941ff263a300fb5c6068ec994422bd019969f47a3abe065a9cb3aa3402b7e2e090c3578acd7f500f13c82b8aa3e2087f46a777ff00b0bb0b78af9942aecb174d4c71f30c088a838307a9a75d004b8b7312ef6f39a7b5060d5079ba5b626e6161ca8fa73596d4bb52c04d2640604064654f82ca2b2e956098a4c563b9cb5387ac0d9f58c060bd580c8aab7a22492e12160427dbc26208bd9954b33a85d27d344352ff8a7c118e813e00c24b2400a81e3d9c7d12eece5fb42b25dfb30d0a0249f673affc4b7d0e01e657256cbb4ec09245c732fc181210e3290db619b371d529a7efdb52de4a36c4d3ec156c5495217e3a95a807d62874d70aece49835c56f7ec186e4f6e2dfb3a9aa7aea871e8e3848be0038df4aeda973dc7cfb66e86ead13930e5d144b1aff666cbf5d109cb8e67a1a392286872ac7d57f7bee8647ef0116698a50d5b8180451f4a8ec46ed2e811f4983320fc570410cc080198ce51c23eb6cb8ce6e7d1e84671d0eaa36b73efa25024498004b6fb6ae8d584106024547ba6d01154d2b9327c2936815ce4638972be41dce0d2a211a97f2843b18159e5328f751dc2ce86daa8a2d2952e6ae2edd299b8c044abbf9169f7e1f5dc36d6e514eb03fb1b0284ca082190b54d425f23944bbfa01574918151472471e35ec17285a2856cb83ac55494860f916b83d2382afa006e3e0157cc26027516e5378e11c1e4dfd5e8c3b4d3ded5b0556e9d5dbe82c2f802cc55183c0392fbe63f45db5dafa6ee6a7ddf1b1b7104648d26a7c61c78913bc87d6b08ece3b685371080dae71ee6538d4c191ae8fd3f9c1cf5a1f3e26d5d1d77d8a517c071a69a0ae1408b03373397aeccffe0809fa9c939d52aff26146b1120889a762542958eed022ff963f96d10b60ecdf3673f8813cbc98a06664e6a8dfb1328158b66c20d457af033442777aa2dbaee7776c575f40117532b134c10627d7a6fdc35950406f820ca8136b4bd77103fa1a7360b33174307154b19422f1572dc6d25e30024d6516576f5bedf603da07cc024fdc9a1ab84cc3f04a95600eaa8bb42a2b8a80803b5b07b1c072246ae31d73aa89758a37227279c66ec1d8c48a560e2821caa5b96deeef4a43a8073e3a6aa2b22a066cb7b9383962338e50ce83786b8c5db418b249dd36594a5d12157443b5c0a9d94e2cf7e8f6581f09b50842b77b291116ffb30ace383750e837290441f4e3c22e31561bf6d7df02b89566efb3390b03deadc619fd17baafb025ab46c0dbdaa489fdb299f5c18c189565358950822d8bc751c3fa6543ec9a1e46b05106a315f3fe060dfcf16898fdb27e8c8c6a9800bc4e25991adcf46d432787655c5fa1be16fef5f740671aea18537c860905558c390f30a32d06badc60791b325512c78e8a4d46478111c93792b1a4187f101b1b172abc0289362f22e98f36f88467a6cdfa08c5175d9cbffdb4fb5aedfa73ddf4e2efad946ee324b079ad1d73fe489040e4884390d56201a16a7039a25227cd323f79f4f79b6742fbe775df7407ccc6f15b52293c239ec4f92245f383fb8614fd718433985ae01402911f5c996a4679834bb375836fe34782227d73b08ebdeba0909697b77db72ef2d654a292a0d1c0df00c53a48c7405574074a67f5dadda63da67da69fa238669199c024406ed728762666666ef51033777b3928ef1d5a3478f1e3500c9a16608911e7c3072e375ab98170cc33429b518334c0bd632d8e32584dd850bd757ca74d8923006c7c23416870049a0766e314ccaf8d9f04b99816d3580c8eed8ba403b6e7c3760bdb0d20033be9d665b0e59ed728814735d49a040f6f696ddb4f6434ef3edb319e2b7ed451d34f97fc031721344af86608008b33a9472c8d65e0d7466666666686a0022a40603447af0e187ce9b01025733bb034a66b50331abba10148420794733a587a66911c36213669d7921a0d4783e72213a2709e1f9c855ec3524efbc66e49eb7a762ba03c93be7814417b23ae76144699cbd9935665dd5198f7654c55425a4f3fd1a8886cba31d3d7124576959d6cf4918676674442d326277172e5c08218e15a43507c3608cda0a52a954ca5f56313a7ad0d4004408ef4ad8ca1cf3f594bf704e601eb1554ccb4097c2c2f11b6f2cdc557b75d111d3ab76fe208e6f672d7b3bb42ba3291f9a557e772a051b7382dbd1dfe8613b84980ac35adb0b957281fe027d053d06ba0ee833b086c622c351532d09134e31bb87707b2d09f367053146182116218429fe7080508ed8e2caf74e485fef0253d21794cc878060540189970b4ae64cc455181826747615e6398fd1ca1cbf1becb3ecdbff61de655e8e698d79ef15bb4baf988da41e7afc6aeae3ec55a4d186af5cb972e56bce4bf6b2c0cf917ebaaeeb50299797558c8e991e35c8af5d4c98d247891efc6e6bb7eb3217044929e544a5fc05478c8e191ade95dbd72e397707d6f25d11f0a5dd1bfb2dab7bb76f5d9926a59649f9c46ab55ac5e898a1a92107213543884033665dad7640c0c6ec8eac6e12a0671e6330fd5f3adb9253b840ca13ae58c10514db8280163d28c18f166900c10baef6156b5b9c23e06965c03746ebfa7cbbc4bc69a490326d0c6bf67c649a96ad564eb2d69ed0a19203951c6a8660a0071f7eb809022749e73184dd370359edde87443e531dbd6229618cf4ce3daf4ba50239e8b01358196ca9ac1810b2dabb0ffac816cb201c698ec6dc9734d609bffd09e184134a29252792d2697ad36272a092838eb402b75869c29775499a1eb0d4aa0ca765eaea9bbd5265d486fe0c42175223d472177422420f7dc8057d6200f4d07fb86999f55102a8bbd52ee859af7a043df4185ec143311e7ab4a27f7d9498e26b8f87be34cdaeeddb6545975f90f9584cc1966c165cd77a51bcc0d8900b6e5fb821447af0e1871b1c01f012d6d14adb31313306b2a4f4770c6cd5d0acf65e29526ad81da36f5fa021bb8345e4ed3ec0d6e6ed3f04c179e759d69e0da3af610893fbf67ca876e8dc731e3af7280f9c6faf728e48c3c90d961821064a34a1401137b83206169890a206ae76cef3dd9c731e44ee51213a173947858052d3b9c885e07cf37571dc719f8a9df31c57f7a998aa76d8bcf39acd3b67ef280e14a63522e7284ee722c7e17cf31cd77add7ddcb72ae8ebf24f05a98dab87b41369f7a1fd87feba5fcf8790240b594cab64c12ccb38cba49665195ca0213c5a8bc2904aa552feb28ad1d183a606204238514bd18ccbbbdeee7641b50b83f97e7f0a2e64a615d3344d6a52d3b06f852395da6c0afcead1a3478f1a80e4503384480f3e18b95996e1d6f4f5ae5ba423375c7747467f239f476fc3574848e443fad2ab1a1f42bc1ffd39e090be85b9dfe3db7ff4a0e1013d16a8f6582c7a7cdb70b479f476b8329cb76b2b2382a7b8397fdb39fc5ac475de68e475dc8723260721353ff4e8c12faf37a0ae43a55c5e56313a667ad4c0bbde2fa58b0c19ca8dde3964cfc7ca928f163479a69246e8b926a516176685a3575df7936db12e2e2f2b1c3a66686a009243cdf288779978fe468f8561bab8406f27c0b676675819e9ed015819cdbd665a737cb3beba7cc7f8c011312c2ef3d8bebd1ffd9bf71ea105529e3f5e8e70b44511e79113c5d5c2b0cb0f474c0e715d5c36482b01efeee82831888787d96bf3ac374f94297aa650f1420042cb11c322532d0933055b2287bedcc72efefa45dfbae62f6cf514c862177df18707b2d835264cf61f8260173b2f81acb3d34f98294f4ff9018ad203f433858779a2f0f0f0c428a38cccc313833618709b9131b70a2224f9ad428aa087f9ad628a2fde7747032d4c7b3bf6fa760e895eed73cdde3fddda5e0dd46004754b00aef61612faf63ea35bfe7abdb8e06a6f345a66abf801145e002db3554411f4edf0d5aded814b9adb0bd20f215ca8458751fb2484ce3cb6e61e2dd59230b9986f775dec9a84addea29bf0ed32bbab6a83f37567e28b435108b2daa5b743bbfa07b2da35264cd70e2594f0a4e795e36ae727232bb850824e9429b08581c18a93d7ebf522c2d54bbeb293ea3ff4ec3240175e088307b6728c70c5d51e81608b81769a0072b5c7a01aad0c45a16fd7a2c04133f6f91b687774cbb7331366ff688efdfa68e18a5fefc7e899f6cfc25461f6cf635f3f438e9bf316635c18f66d19483bade4a30560f46a3f7eed9f6f3ffd56e1832b7cffb44c655ee321d59230699aa6b19768876be65e66516f50fe7a84be9e0dd0bbb12cc3b428e1d622166d52e346bc30ebbb63650cb8e8ab85590ebedc7cf711bae60c3d730dba06bd88166195bd523e742e02a98c5f70db321b83ce5852d7878928bee94d3f08527f93fa7624f26db42b6bbc9e3d87f9489c79cda1d79827396c4d879e4fcd4923ea5109599a6f50abce5a4e6ca6424d3d74087dc2967428010d4c314615b4b8c2043b80c2b5f97a12a040167e9210818a1354a82025fab64f4256e6f29bb0a3710898d3cd4e601b3b0c831f742c3a3b16b908f4a64e6c1b1d028aa67953150eedd2db25bde167ba2a1bf8a3ab8248223e3a7f7cf9506c8a40dfaf395fa3c37d1dda397633914add743f06aff01550a818bb34ba86dd1d81950f5d3ea42a1ca2438fb43b324f9132c64a4ca6698c611fdf6037d86710a0e1ca952b4c060ff150a669317e1c049d90920eb1412e28199f11e34b1a2f2dc34c8a8e41e798eb18ba25e0f755f98b7decc56eee238066c665cccc39e78f9bdbd6759de8e42cdea8eb4c26af2e2678a5e45dd775236e3bcd6d9bd408ea37cfc877bfb577f28de3b86dd419416d9e725971ce0e448ea8731b2d437e7b7139d16a0455512f12d5d95512d7719c11d473a817a1b86de344ded16ae43b23a8ce088a478e8d5cabb21a41bd473d80fa225be7d649d9d1ee0653feb7de5a18d91f266918018ae1a22c81341a394db3b0d492732693a9e4d239d28e4823d25764841ac5de584e24d289c4715fdd1e96adbb9141c4d148c5426fc879169613cb8992485fdd1e9293bacf446bc9db6f92f347722683f38a72138974a23507a7ab28af9d9bdcfb6a918e4e97a7e87da351e725aff3923772cf6960376d702aca4d24a7e9ce063ec999e4ec261429bee7755d7ccf491df53c2775b4fed0ddc813e944f21a0489147be53989e415e73b6fe74e273a62a1db2bcfebbe12add3fbd649dffee8839e73dee7d19f2b4b203109096e5faa7b9ef3e739578928af261f9d4e1c37f25aaad339140b8bc96462992e1de58d7c4f4ed3251bf8dec9fb8a9c4eeca5eff455fe53ec8d8ba81b375a6eb4b4a050dcd6ddc8202210fced39d7d27223e54a6e6ceeb9b799689d71ca0e02ec1e0bad39cfe2a5afc62fe194aac96bc947de7db54889def4979ce492a5446bfc9273de7da7affef0a7343a35bee41d10b0e4d34fde390d2c7dfba7af925c3af7d9e03ccabbaf9a7ce4ebe180f345d83da7e9920d7c9487fa8a78ce3e32d19b7ecff4b1775f8ddf75255a71be648adec9bb71c36b5a6eb4b4987cbd437947bd12ad3f7437f2e49d82f03c8fc6d35759dcf36d9993e72a16af385ff2768ee54332c39f9ce38ee553ed90f216afb9b1c3ba922849790b3db9921b6e833a649d6e7ca96f216be4ebe98052e160f2939f6815e24dbebfde99becd4bdfa495446fbafbd6479ff7ed633e543bdcf094d7d8f016e7b8bb415336680bdd8df61633734d0df9551c26b8a9079a73dd84996d3cc6ccbce4dceaf06dfe55520634d7c705529e7dfa7a3a745eea9cc5ebbad3e974eabcc4e21d8af759befad9f04f2f32bb2526893a47a4f2cff6e6574f3e55277a43ce6f9b57a2153571808ec65c1f1744f131dd8a2ecd054fbce6385ac6dbd36173f6e5724427b7e1ebf96839d1da7f72938b44ce7d95667a9d79d1f41a33bdae2610345cb9b2c69f9ce4a2b936fc54472eddf4d90c51f94fdfbe8dafa29cc55b3e1bfe92af67c356fa8a6ccecef2754b4c146ddfd2c7222f0531b988567f916f5fdd1cc90c7f9252e4bb2d6e731590fd13ed6e307dd24ae4a7b7a9e4eba2d2e7f9fc48b48ee80d26fad6371cbc1b72be7324d81759ef5c762ee59c1dd77d2a20fb406cce39e7220742e4eb1b5defbeeeeb2ce6e622e7826ccf73bdd17ef375d1578d70d06f14097cee3566e6fc517e95483761663a7ccf5a893b2c642d68826806dcbed4961f665ee6cd2209451cc1093e406862c7c53936a0c21786aae069028c242e0ee3b90acf323c634c669d93e60727fcf3e2e9209d1e38e424febc78b0209d9e6cc889f6f3e291413a3ddb9013d1cf8b870bd2e9e9869c783f2f9e51904e0f69c8c9fc79f19482747a4c434e4e3f2f1e54904e0fcb508b13d85a57cb8f134ef0b5f5d18fc47ca436a42b5f973d1f363e1fd22670120c93bde3a2f875301ea500ce869f9d79a8b8c28347d9c4775918de2390760bd3bed45b9834be2e71c657d924868c0a737da080f1b5fbdd010c7607f4766f772cf7ebb303268fea559f80b4b3debdf6aaf5816208bf3e50587100293d0042e9d7e78929587e7d6eb044fe4ad9011a3d40e2092c62cb54ccd78bc82accb9f08b8e7df0374375c30cd5584db9b412988d3c7711e61a41bd119416384a59e714d38ecad4d7f89a63b093a9be024bac6146abff7a16635c6cd29a7a0cc33c3e26139dc330cc610dbd6aafd1338cd618234dc611093f4078750e86e292873e3f78e261377d9c40e3d707073cbf57fcfae0a0e751317e9156f8b19747a49376f0b9e63c3b747ee99ec2677bbec83a8c1b638c2c5ba64a39672ae5beea195ab1edf9e85c246234d29cc8980eb7bbc3eb7639d1eeb6be1f6b1916bbbb7d2ecc76b77777efee2eafd771a3d14683753941afcfc9bebbeb4286cc4c996b04179198ebabc52c61ae8f13afafa9a1af32e5b22e21fdfa9648d7e6da97a48819d7e64ba44b2412c91f545073114857c0cbc408db1d3a15221dd2852c8a750c83c3bc98ebd344d0fb34a1235b667d9a00faf6ce25db79897449dfdde184ccbeb89e15b9e98f14097c2744a0f9e96562840daf5ce1018ff0ef952b2ecd6ffa9dd07c3d1e68706974212ba680db95894d2b8ddf42991861c32b5778b4d7f5b847f8f7ca95f69bc68186cf810c8b80827122acc33e8e963ef0d04b0f310c1b01f6d08fd4f0ebf5c29c137dbcac2c834e836c0f5d588771ce43479d87f5ce499115fa1c578eab5d8abe2422e7be249c774f4c9bb5f2ebda4580e6ce36e43ce634100e117150c04be736ee2ef4461b45d2af05e9d7333d72333f73de1c1127f382c4e77cb08b8e48ca0b93659ae7434a2dc322848ef9c034b8852d28d1b9e8f1b0048acdcc17597d8e6bf3e805d1dca632e6db9764f322a0d8ccfc0d4440e6f1c36cc8f9e85b1da2c6d36b5ec31be5656d21f87686a279fc8e6023883408f6dddd8b8d2045d29bc7afa65fae8d262962ba761150570111e73b48fc1622f2ed5bd2ae24d28b902e11e54500e68b39371424fd9a0d7c0d8a4631ca594722b87da9d9eba17331c6c83db0055d31744bccdef938b43bd8a3c8a37316b677fa041f9b888ffc85cd2bffc8c841d0a1c87b07b63020c882fe0324699d4129a1da3b0f7bbe760f4697c8634350e4333c4663af228d49441fb6588d491e7a8d3adf1c93c4259149cbb0672f8c082e3a87f1300f8cf24f0f980be6441ce79bd9300761e65b96f93a84bef97e36433c7408b5d911312bfffc4621b8a465a447874c5a66f3e85047e4d1e14ecb701e1d36f17cabb8628e2b6e956e4557743e6388d168192bbb236a5276f408ff641e19f4dabfce3fcc23a36d85b9d81d90361816267a6c2fd3bcb6b230d16114264eafa207d1abe8de079d4056f4f67e70ffc3c244d77e8c3ed8e4833a1fdce95574d10791804c8460abad54197d295cd2ab6844c664c2e85e3f99982ff330bd7e7df62c4ce4812dc813773b4c68623cd1394a74ee611efdb104b3eed047efd77c58985817e8238fc75857cf3c30ba6c5ae58c1ee521c88a1eaf7cfce0abc96caf6de5635be955c4b02ccbb24ea35711be201566f4b6f2d3322a1bfee5affcd14a5b890e5fd1e192e850077b8db6e8e9e42e8470f7eb660966ebe8415330d7ab8e974d23d0e4c202b9e22a1bfed4bb24c13ed3be8a99e920ecfa6ef7946f6f8a93354ef40ef28660ce5455c3bcbbbbbbbbbbbbbbbbbbbbbb53fa673dae63ebd9ae0bfbd615bf75c17571cad757eb9d44cf6af103650b3a5041114a20c1133e5c0801075c1c01a80bd7baafcfeee929fdb3de4071883b4b8117cfa9946f15fe1bb87da952e7db392ff4d0b7d7ad85b0279154496f85198120ab9dc463be7ea3d0c2781cc258b02dec09132d83797bb4b23b326f8f5f581e9a4e6541af31da8ef669447cda6b61dab14f7be2096c69496c0cc3b6b4122cab3de66b462b1604c88ad89354ea74fa1a87b8990767279855f3ca3d5a106c693b99f6e4dbb5cc8a9c3d5a500f6c69afcc4a0fccacf42ccdace8ccaa0559f9765ec1ac9811be1d46d9812ded07b274604b3af9766c8c6d694141bb83bd1d8bc2f2e8d996c6035946d81d98b7122cc8b3128441a3756e21d382aa90029a3f5fb129fd647251b68c3f74c6981ee16762761f9f3df68e7ba230211840bd823f0b337a386561ba5d1c779029c6c3aff1c056f604b2dad90ad482608bab5856fbac1a4fbf7eae905dcc27a4c6d0825ec94bca431ff77cecb339dab325337a8d4030ca936c0a6c2dcc9ec0d66a05a7c06cca14a9d12a672a7bf20d9740563b3ff99a7a4e6804b3873a7a61aaf66b2731e7afcf500e6637315ba887dae10b7a2cc1acfc82241e5e839847a4dcc339a4b03b983215cb3c7cc00624e9d9fdf6011d139a35027dbb0fb8577cd14410f785ab3d0a318f7ea6f5480ab6b020c86ae9d9ab27f55262418c053d7449ca41329855aee0bb85dec6870dabf1d5e6a307c18e7ca49e0418369952a7e5889898ddd1420bc3fefb52319d6767eed6b417ba6ad2c20550174ab6b0820eaec4e003202821451323a438829d10b33bbaf38ee2705455d371187430e4ea3cc7e5798e8b55355c1c822c76ef8b4131414e9ef044e99902141413146305bee012a1216c09f6b202a930ab3f735d0de79e7b14c7abe97ce4238a33f2ce735c51063d62608be60acd95a15eed8fbe85634cf6ee9b6bcc3abfbd760d7e3ff6bb1b4cee35df9e35807ebd1fdccff48a59c7c70f694ccf60fad70eeaa01eda560e0fb0d8620b26487022892fb8d8dbcab6828041126428b1021d08c1898b1d9e311db63aa859ecdc415c8d7cb750d55e736c79c46c0e76cd653779b5acab5c61d60efa2017899c77055d93746a22872e9d137df2b7af7bf9d59867b8cc8d8df6b76fbefcb4afe2c812f0fd19417d0bb5cc173f897546cce0c90c9c409e5f691a8661580ceff48a25edd63426acfc8d0ff9f395776470860caa38e66768bf473001d0afe703a335e6355a63b02fe6fac8e089af70870912cfbedf640ac844b7d63503a1af10c219043d3b849bd7dd60aec71fdebed031f8cde7a2f7c3fbb1aafc5914b97cf875cfde0f11e5b4309756ad6a8e49d7707cdcc425939669ca4e90681949631c66611a61b66362cc762c0ab31d0b9ab0e50d4159c210a4d081bc039398d811667b2f319bd6606172ddc4e4169654185369990a31acb55c386182c933a9491464b8d8798a96591f1a80e1d9f4ec2c45cab3f34fb7dac55aefd060688c67874e602b0994f57132c54be1397ae66a7cbb60faceb67a88b282226a00c517bc70818b9d79b69503031ed46005599861c4158ae0ea1bcc0a993c3b64d22d87cc8579e4663e8c41129b83bdfa125264d8e14e1498e8162f3105dc7162e5d9898f93a167e726fc44d3480f771686bd999831ecf4071dfb7e1842e731efaff28fccb585a36af3f32d81762d64b5f6ed67417c37bd5a6ea7572bd8e22496c5be440c0e9807cf2bd85a57ccccf6f04eeb4ce884c226bd62f6686556b8f3ec3862681bcc853c24a03ae7eee829434e983b7a7a4aab6ae442166f9faa66c39190c5a3928a07d1e6225f978a87ce39e7a810d23b5fd7f23ce6952b820511a9826cdfbe7d17d1bc3deb2f125d65c352c1e4e7a3475fafa960f2c4c36ffbef87edd7bb4fc5c3c8a54b2a44e7235f97e853f1408570ad37c4f38daa6a36ce3749e37a43bace2555d54817b988e2885c7a8e2bc735a21108b27a208b9da635264cd6d971f2244a4f0c0dd44142ec710a7bfc61a1a067a067d7a230b7af7f1a08b2d8b9af83208bbdfb5a08f61064754fafd6d57deb5ad7d64da677cad95771486b6d073674c69d659d712f905e41e1b5b3f2548cdcd6dc1997c4ceb8b3ac336e202c80e9d1609af40a66477a6d01dadc069396a92ea5b700750bb604b55469116a196a41a3250d1b2f1b496c2cb1a1b349df59c118016607722d51becaaf2d402d4090055b7a3ed21a23f2ecdb562987d9812c29698d5e5b801ebabaef5b56e6a2af5680f599c3ecc09607b35361762acc4ef76ded0561bd740e2ef76817265b97e8dba0469de03cfbb886e9919d25e98c3bcb3ae39e3395725fd59d22042631a1e0e2075beb6ead9f83dac7bbc29cbd2018c7851826e13ba17bb9479eb917f1f2ed47eec81cb923e4cfa81312934498295fc5d0c0d66e94e976157d2e2bfab26296159d66591148cbb4b3dcd5177715bdbfdd55ac3f3cd6c9f819b5c1792c4631666af60aba3c742e876651205566756f198ca2fca565e082c141ef6eefe65eeeeeeeeeeeeee5ee6ef66daac2bc553630efc274f737d6700a9725616e8989978c0d89f0b2c2a16386a6062039d430efe87983103e71d2de0570342197f48d2e64613618faea127d6c20f4dd5b7b125897a4db2d98fed545873280c2f079828411689023c51a307082451090e093c4d53e5af084cbb6360b55788110d288010c4c30e46a7fd9560d27f881c24917cc20a30aae761cdb0a410f9ae041132736b08114ae761ddb5a32787686a0808211c06002573347d622bb58a95b2dd51b0ea73005c81420426a8610e9c1c80f3741bce0ae562b292bd4aa1b25c6aca88f11c550c0be30d7c541b1e38346cfcb5557efc50432c503a7afbfe5abd3451e3d1bfa4d7e62a12547d18afad2e933bdc94db408e7ec755212c58173e6f7be7d1ad87dfbdcb7a2f5ed798d1641c2cf48f82b12cdd96f348a84bf48078ef0b30771225ef5bd62701c1a62a050d8335aa47f502d12d291f94851c5e3368d4c25dd6ee7206f8f9a5909ec878e856199981f5dcc3a4acc63867d1d14d5e55be2c034af98432ab32f62195df5aa338f2f1af42c73ac33485399c7ae575373cc39f8759aefd7f81a4da182d698d0b5d5ba0ccb6aa495e5316aa4fb4873beb52bdcbe64eda457cd4416a3635f3fe9557b3f91edccda3ccd444f692755f816bbd6470a30bebda370d232fc82d92de9add38491b0c1accda3d351b44c744cf64f34428a28bf3e43787d8e5f9f21e8fc8b6582d0e9553b23d1abd6612566e526dfdea4652aeb20f1af0e07043cb03b9657116098fdc0baca0883977084062cc00b57bea30cb87d592d4c1be09bb5393ad5ac4e311731db6b8a6738c81c312c321b31612b765f573160a0810691294c61b62fc62f307ad080a301387a2c0ca4014703702c8e15cc8ae33ba8833a688368608bab728b0e370a2325224484b3823bd99531db1713123df3e4cac59e40050c843e0ca02853869edb2c8cb93e42087a6e3331e6fa08e1e7395297a17ae55ecce97ac5d1f78b79f9ad2b855a18d8a88581361e7a5500cbe668d7e8230b269096a9297777f74fadbceb7ae5a8a159e78fb9f50a47af72a2af173df2ccb28da95eb9cb9cb8a5752d6e4448a3d3a0f2b0bd8c62f19d58e22ecd9dd0e8102817142c523f783d7bea072ffe420d356ab0341db9e53c11ad3899f4f57c7811c86815b9e69eaf6783e652f39d4ef20b8cdcf36d9278d44568056d282626867b8618fa762134b04e4cd6e48abcf2ec6540b6e582e1046038c1cb083ada7be564d62983b98b4f9980d6c1f946636dd770a44813798d2f696def166d9e51ad0ab3a234672f0829e863d043efcf4884aff942047dfb103d2219cd339b19ae5c49a39de4c8cf066af4db031bf4911679811815bdc66c0fb23ecf1e37cb2886c3b2df601f895005212da1f3ecd15f446041e7256645a55ca0af76477f5019dd42dd420cb5acb7146435118ca665cf4e35adec29b8d19acaea0d967d6dc6acabc54786104aa1d91e9bd0ce71c38869abc66a66349f1009e48a9920333cb3c6ac739f6899908561e736d82710610c064e7bed0e4864617a48a1e2c53318ddc6c4e3388dde195a71926e61a059ec699c81c6739d398391d82a4ce00cf487b4cebc461e80f94d6fa260567ec2685079cd339fa1812cf6ec23025b429ac50e241434d3bc435f67602bf3197ef203f9c94c7b7c1295cc3af324cec099cd86af4048671686ddc90d2a5cb72a88d91df3f4ed550108581ea5cdd1a35f22666d1d1f8458cb62478064c0b3fbc02fccca4e9e602a52a688c28993c856666520ef24a96634be817637354b100359d9e4166230c320846d029dded13030a44900f443600b870e20e911cb0148181847af38a659ec5b8f990c46c83d78c9cccc8c08c78abf24b3b64e94d6691d1d1c1e29c7753b73b60e6409b18e467d681dd8fa61c9b78ece0bed7c3b1761c4608c11760a445b84cd52cb961bd59ceef11f701ac70d31adcbba93679d6fddb60ce6bbcef9fa9a46bebe6a1992afe36899e9eb313ae2c2789ec8510ebd8462c8ad27819c18f010b185175801e50b315c9f476be9e36f5871fee4f033d2bde9642a7dfc581785f9c95d795dbd217d317abbf23aafa3ec9c8846e91d8d50c0c4a012658c5e64a3acd1638c51fac26d730c1371954db4ad6b523af6ad4b2c7a3e960ee915e63450f3fd30ccb5fd5804309e8378cc23642f887ccd63740c5b6aa3bda6c51ffb516e8c45c1c58861910993e7209765beabbdb636da67187bb363be1f631f070d818e7d2c05ceb1dece1e84fc76aed22ba68136da778c391a633a5c3b8a69302bcadb71c4e868195133fb7a4cd3cb6154d3b63a3f85f2a28b6835f22291cb33d32af2a86118e618f47885099da56b996e49855953dd7b45cdd91e63d6f8ddc566b5b714e03792fe235a7b30c2a843dc55868d200816b96186514845105e41e0913ef8e1f5f1c14b66b6d6832176f9a0376cc8914e6ea64164c8912153890c3932642a912147864c25466628405f48bbb1237863b85bc84f439681ce5e49df1895594cf61a31ee3baeb62dcc1ba3a866b5b757120a15c48b45b8bbbd8d60633b9c11b32e50b633eb2a660a902a2b287ebe1d470fcc03f3760e308f0df3f5c83ca4af67dcea38afa3d7d8ab0c1d5d719a8daf2b1b3608f135c789296c2011f2355a3d60c6f38fb562c8060688e1d9f4141f36ebf322e26f34ef419217b936c673e1055a30e3d935cf862d638a67afd20331681046189ebd0c29168c279a10c6662184c5428ba5a2c56611250c3040a0e5355a73861006038cc8918568dbb66d1369a31a64090684b154b280f962a5051ba8149e3d0625a52c6814b42e7a1040bd46eb07bc286d72935bcca8fe608467e744dde6fde0cef85105c18f080469907aa60e9e6fc881c50fb98467975e90fe8a448a4d6edea88ac00bf28b97de0f4c8967df3c1b60148ee33891881389384e2412711c2712715dc6a8feb3b77743b6e40651099e9df382f078d686601f6f8ecde50737c7e62bd8c296b57975a18165a335e65b4431d02b0d02fc224ae40b13a347fcd7fb7180b7d92b6b3c7ba5dcf6f17359175b061e4192d9d0f022ca0ba3f976434e90edd11c7a41fa25258281c640af86ac945709be872f263711203e3c53625638aa98bd35090654b480ca2f152dd031c02f152d7022bd98ebf9884966f47576e955af70446fb325737d78a67c9d7124fc15feeae8d13218dd1c9973fba5e6d7ebdeabf6679e281e0efc90be7c61aed34ea536d214ca3f9ba8dd71647b1e49bf0da7c1bbe30812f998df640ffb33e8fcd9c78f519bbd72e523f5d182259c63d500c2a3d8f4a4e70ee45210a17861de167650e5515de6f9d8203ceae3cfd633cf87f7494df37cacffd85f1a435685b93e3b986207541ed52bd9f1c683edd767074f1eb58d9673acc32df80d9672277283dd60371884ee446eb0e944be63419d44a2af122149392711918805751a491389e4cf99e60de6cf3927495ee788e6a1137108892cf4bca3245ae788d65425e2515a53a91b6ca66e886c708390831ce4e006219432c541e87e83119142565240f3a7ee14af333f6356bbaddddd4db94b1b7f24316cd06f4fd0af6fcf6f907b49091c7b3f2aea453f60dd749092a341a0b743230f5122971c05c10cefb9f4edebaf21bc21e73d2ab7a337e47c46bb25ada5ba79d36ae48da09e49ee7d15f5dec9b455d44b1f6d9ecbaf3b99e49c23a781de37a2b584faf9d512c93323a8afa29e736e0459d856873cf4edab467e73d46f4ed35044ab9187de9c4b88f9421f51cc3d5a8d3c9499537a67ce7d46503a684536dfbac7d6b9c8e5ccc7b85015e79e8f1c3a4ac55120fbd347ee397408a92a73ce37af246f9f1f5565aeeadca3aa4ebac85519b7f9e69273e822177d5525822208a17350f3f51e2b25f3cd4531f5e55da84a7a87a22a4981ec8fdcf3cedb1736a42acea5431f7d1e5571aecabca3aa4cc549e92e4232c30391f3d2d737dfbeaadabcc7af7ea340f66b8f5f5f7d8f5f97df7a453d127e6c94c6179ba39c437d9385d6f82cceb944b9e8ab382f72ae6187fa448ee24acef29150df54ed70c3537e011bde02646fa4e842d6892ab1d1b27dd145175f74d145175d941a9bb29df47e786ea295df44fa2ace934e55e4f0ab38a2205ee428a7e9e9ebfd90b4720e4b2e3f123cf97a503a8a9ee8c825ad904ed7bef8a3af428faaead6b9e6db27617ff715c96eba087741c91cf362d6177973319b8b29a2d5bd98edf2a16b9ffce662425a1ffa6e73312ba437cdc56c0868cf2424fcfd3ca31852e27cc55cbaf61591f4a65f46dfaf7222879a74516f241f798de71d69f3d14617ea80dbec099323adf7a3c6f57ad35007110da2b93396f322bfe9175110ccf022c7f97843ce8b36979ca813751413b9fc6efaa5a437e4bcc8a18b441bac380f2945c2cf0e69c5f91bcf11713f214adf1c9322b951d5ba2a6a32dbcca3a7a48bbb9ead47172ae9a843d6de3463988c1dbd914795745cd33844ccd93a6ec175196b4b81ec738e398b38912aba2af3566514c87ee6aa4881ecc74c6630464d85b9fceed76de22391e181c8f9a8c268f7126bdaf56a61d32862580220d833d50ea3ce6b3c178dbc73259e8b32a939ab761839e7359e6f23251aade99ce3a812d1461db2180bceba8fc02d4742d25f64d959e49273fed6396ee3441b5d0d4a8dd69bf8b01a7179c81b739094520b2db46062647f52d00e6977832cb2c88283346602c47a165724922e72259ccb0cc90c0f7d87aee33acc3757c251872c8c6a5954ed20f2cd6ba46b22aa4452871a75c882586081c510590eecce825be7f653ad2fd5808051c64dd2cd1cd332bab46ecf63746b64e7a632a1578c1a71a991dec4871409fc9c8cd90c6e5db8f57da90c04747e2ac6b0f2fcda7124c6612f15558cf89b2246b6ca47ea81ad52e521e5305e267c19126691f5fdcaafafeb5d65c9330efc7abd0ef3a3c30f49fc48f7ab39af0a22fae8013e8e006361f66337bb06547e7db6d079d6afcf164e7ec2166fdfbec5ebeb0cebf3f382e7badb7e5337a813584d6435a471e30be6fafca8e01d055a08452b4430a7e99d025b983774f8f303041414c4423a30569ca15eb510fc4ed2aba6f007b25a89593bc9b777929659d22d8f2f28842550591f2da2fc6a31e5d9633784d0f3b18e84164c5c01e5e612ca8dca19272633e74387402d087b29b71f9218042efc3423263bff664998f1978a29f8e0d93952968489fd5231851d3c0b00f5eb83041e663a6356f34ccba484718686df764f8183be63d5f8ca2e23ed62d4c15c07435b28a486b38debe2ca46c2648f2b57325abc3e0b2b90b5bf7d2e29396572af9cf4d7a78a35b2e0e26b2ab280db213c33760514165021410aac50868bb1d8c10c5840c5172c60c2135c2bd8ea9f181d2dc3f2ecfed232521e7a842ea728f1d03b14dce0a1bb4cf9824c031e2601080f1d07f498133c741d3f2ee83d5a66490f1d88d043cf418812ac3cf41a1bb8a06380480c1e7a0f2dc3f2d07de079e846a0cf156820e0a1077183878e0377090e1e3a3381eb43458d87ce48402cbc78e8dca46516e8a14f164f3c7466a265460f3d7e7bb09b67204725b874bb3efcd02d040c41091268e155c6145d70450fa25b394488829527869082204ce10a57dc2b1f9d9f1c31c647e72754e08ace51b40c74eee129bb839f8f584214603479011951aab822189898310331a4d8820a44b8e21a1f8fb8f2313a901c84c0d674451f4264598c095d6e6b5da85ea5b2b03f9f5f500a5ec20e908999ba81f90c0d27674ace949ca9de81aa1df31c8ce2883c739c6d35177d423e55cb8f46d5bea99aaa76909e798df40c074ad39accb74cca281dca6ee84507ed0e6c6761a44bade7a5d3c016e682c22e55e639ae922aa342924c37798e8be40b2b0f10c8ca9ccba457d68170947d75e6e593af988ef44c72de65a6aff4614e204bfafc48d893972eb730d745f20959d2a1d434949a919328cea8a9af4bba6caaaa1979fbc8dbd7a5f2a8f41564494c67473ae6443af644fae8d39e409674ccdba15d980e6449d79830e10f50106ce5b8a4432138241d5a5922bdfb385ad98974ec09c6231d8b225ddb91ae4911ad5a948d568de7e5939798ce4b97b488d693b5880b1883c9cc9cc27cb54f155d240428c7c56d9ff6d1a8a28b3cc785b33052d50e9a6f5ea3f9e691d66c2ea2383190057d731ce81a74412626464b0be3ca38961ff6c575710be184ec10ba843e21ec2805d7ce35afb7cc17406acc408c35be18021a6a8421cbc9c111c2a086174918a1e488be428b35d080a202269670795d20010fa080b10434c270a5810425befd654600bf3e5670c18a2990a0ca4dc52c93b4ca8f3146091b8bd139e6604374b7102627352945bdbdbdfa8ef99ee1cc351ab34f661946abfc69209a65ade76c1ca7e884c438c13e2331af654e200dba542a76ed28295d6a1475243e468dc8a6e2e7a17331c0752b0f1768ce2c508c84af2190051dbe608b8598c52ee59c4ca30c812d29e704a240b5a33cf4651ef2b5de10b3b25013cc637b096c710ea7740b1e4e227c854247640c819847466110e7800e1d3a61773085525818e8126817e75008b6f88a65314fe11cec9095f0cc424ac6483d6142a19882871ec9d816f664a3151e462e3c8c6a605e31250fb1143cf48ab9e0a1ef62327888094116f49488d6d49c9810ac5890462b06f49156ec079bc2500a3028e3cfa0802f98d9925ec1ca98106c653ccb821e65a8e7a167531ebad0d0432b0f31a10c8a8581994ec62483e2a1472b73bf664bb8670bdbea615942ccc3029b831d3a5761190b2b133d0bcf2e148687fb042a3ce45ff78865be543c373164613040047eede2c0f819532d53a1b3a37a95aa53ba30adec10621ffff5faed612a3b6736f320e8d9dbeb047a27cc66ef28b3bd6e1acf1eb930db39898443df431d51d0cec118cc211820e2f9d2f99302821c0437e65b054ff8750b2c0f66c22a1cb222ebfb34dd3b369cbe9f28912d398ec24c984723c139da2533f1287c41563ff9f68e56a735b50375bec1f8da4c446927dda47790601e9dc4e6e8182c4cbba4487c3b6682c958e67ded825e0ef16df64a867d303d44e6186f8e8883f1aea2631f4780c9e8b146c8c3b57482db75b93ed753ebeb006f97b5181afdfafcbccce0f6a5b2e4b28fe3c2100f2c7ad0d3eec23cda5d96055dde2588e5978a264d1ed23aa9f46adfe54d9c838544290318aa620d6a25a60aa167af01c89367cfa165e0b3d74448c40753189e1d6532318f8df3f9a5573e45ed16c5cc0973a570610a4ec70dd95e27ebdb4bcb6a395decebcd85e91e33bd5a1ea2d7f3733385b2322175425543c23e9ccc852001e61955f1a00991b9465536fcb2eb05b4af2ba3ec09c1d63553debe6ad8900932c608bbbbdb65cb7c1331c5771361c5f7c8d5ddddfaf614cfb7bb100184ef6ef912615371c5b777b7a8d8e2db5fba63f8f5a182078f3d9149f1a400852b4f744109340882abc1400226bea1ab7b60e2043c6278c1042fc882cc3056cf0cbebef4442b4899d651c3daf908667c7b4c75017e7d8e40851ffdfa50a1f3c3832c06cd031d604f60a532188359d5ba960d7c60c50bccd8620d25b8b21728e9220b662c71c20f5cd917b0a0c186c593a85123339fb98e9f5963626260abcbb038e2b32c662e61c6dd63df0b6c94cb8d5f9f2e88f0f3d7a78b257c7de9c168c02fa8c92ee5a8ae57dc3e7b9ace1f7e69dfcbaa513a7ab44ce557cc4ccb5891c5b747d93ea1155f7cfbcbaa3d86a609df5e03907621ed43da0a297c7b0f2d837dbb0fed3fb415dda15c823057bf3e5634f1a65f1f2b90f8fab2820e128a4258c4ce28a3ab74506ca11612ea253162d8621847c13cb0751a8966d24b5a05add33a3e88be5f8ccb3a2216b199fe623847f4f67c2cc7dd7ed1d7d321019c23461dbde24f47ecdd88849e1e29f4f0f440a1e7a77654e036052511a2f8f591228d6ffdfa4cc1e4d7f39159316bea3b665ee1db6750dfdd55f8ec7cbb0b6ab64c453d3fa0c7caaef6eb1c5ad67e950fe8115ad6fed2cab45b35eaf7574ad7537030c208238454f20da66cc2645f661250f1faf5e9117a03fcfaf4587917cd01d231c7d10ef066618e6111c3d87331c1d468aa579863de987760cc085b1bd3517a9cb477aa7b087d665ae1a1bfa4d278087d5f2a1733f3f18c8f3ed3a365a0f74f8c31462344f131fa7ad0e3f66cd18459a3ecdadbe1c26db84ed3fd2d906c67aed7c8bfdf7eb73b8163df971a0393cc941ca3c52e9f22a4f1cd1be0202c022973106e7f77a9acdd778c47187ecf23c9d80b7a245dc7f7bad1ee38c2afb7a1e191647f84871e89e908fcda7dbf5ecf3ebbf6820852c840b1689a974a2c2cbe9e0d161b9a7736e80d392d5a8b8d1b2c366c9041060a95baa152918142a16ea0503e6a7116161bde6203e59ea76cd838a57ce479fb373e1b7ecfb99467327128efe34f7d35fe447d353eca8673366ca01c153d1b4c269409d5e5600c92779fa74d4e1b79d53cef6b718df35ce35cfb6ca06cf05cf337994ca692a6b9c9860dcfb5af12790de59c7b25f7a49cf354a2de57895422cfdd2879f5dc06ed96989eca739373d4d3386e0c13e71c8fa1611de75a89d3469ee6eb37beaad11bec3b6fa41a2206c75151289d43a91dc939d2181a89a47da619660000006490c1c545a58a21861b3752291b365a5a585850a8d3c9642a95e624914623cfeb3a8e1389b68d85a554a255a2bc6a3b524ef2dd1d25afd8b3b80d12add99b7ce4a96f1766446bf69aa3bed76c5094b37cbb30285ae39b7c57a6e426936b5e22f2f5062b7283bd562a9568f592e6af75ce25ba14878e7ff4ed3b911214aa984c26138b771f8b9b66c93b2f695efa506ec3357af2f8a693c9b9538b9f4eaa1619349473dd953e94afd7e2348df2ce3bef34ef5c6b71d50e2a950caa1b2e275a715e73194a5fcad7d3a1f4d5ce51b45b627654d5b9c935da42551d0d52f29397fcd4cd2aa62a2593a9c8ec26ada752c7c262e3ab265f4f7dd5446f3a06c7b951a5e4fe259f25975e6a69c9d95e95e8ec3739476b7f8ba764107f6271d407046c711b3e2a95fcb43a4d6fa93726df3cf5d96857ae5c59e36db80da7e9966f7f648322e137b9c9d939d357720ef5f173d053ed70c365f00ba8dcc503c2eca7afc56f7c32b8ea7371962f7aaa1d6eb80c5ea37217e7b00e158193b7788b2b51790bdd5eb178bd69d3d7ffa6af5f06bfe1352eaeba21c7e427194c7ec3853a6499e8f66aba2af66a3a4d9bbe52a934bf7ad3122786ef75e3c6a9bd2f32eee32f394dd7b8c3f35af2da2ae870e06ff1ee6bf1d211fe18ba256611cd4d5cf7955cfb6e78ca4b282f995c9bae79f755d4775d37519a97bce49a77376edc70936b5f35f29ab76703ca4b6e9a27158ade90f3b3e426d74a5f35f2251bbe9e0e279fa6af9afc06ed969826aa3279e725da1a55996890934f3ff92c6937fc2cce7137ddab11d49fbc730e0807fc0d7f078ef09f9ca63b16e78e685e62a175d21345c25f6af11a3ddf85a99dd756c1674e03539f8daf96b6cebdcf09112871284590f0102ac7898142e11c4ae51c09ffc83993731926aa0ae26f727f9397fc865f15c4e4d24d2e47fca7165a73bec54b9fe9ab4650ce42ebfc924f477ddbab49eb7c93c94b1e432b0647a16895317cdbac49abfc5235d252a48522e13ff97e3582e2ba3b7dfc27166f2f88f49373a7cfe41cd7de0d39ddf2f1899abce59b0ebd169fb4a2bec56960a712a8740b978a433d0a9942332302000000b314003030141088c482c1584015f73d14000d90ac4a644a9689a32488619432c618628c0100000020003333a40df635ccc233fcc116b719703863a4f80251cbc14e35e72992d4035737703ead0b2e916745c401ad68218c57cca910c0764da566f61a8f401cc5f356334e159ff80f6eabe5b15f30890dc1cda21c5f51cadabe5a45c0a77643d48263140444cda0d8202007e7a89d99dee0e31dbc559238142851403bd2b585ed9dfef6e785c8740c86008c9ee508eb00f8bc36747f9f58cdb1d88aaa3d5d313a73da8b1aa6b1babb0b5833d690ebb694b4f81d847cb2e39ae224ac9cfd2fe886d845c0bed2a1b2366b7514ec9e467df87c0a42bd59af28a280cfee293e79b23a6a9ad43527212b46e1dc1a3392b25c6cc1ab534a487baea1bbff245630b91be2e4a4f940de6b73da1dcac182e0a377c5800056fd98a39f0a98405f1e420bc1b104cb97513f7bf54dbbef1a6c8d97305cfd91a2d62488e4a6ec3d7718bdd7e463d512120004febd1260ffe509c69515d059b20dd428b298e1dea1cf8e8423f361661be94596fb4501eac3f5a577cd205d3fa8ca6f99ebaa4f2ba1cc3ad2f2ddad942f367d5a6b123d4976aa17794a92d29f00f2987e3c01d0f58efa24fb26f01b262aaf4979b91bbbac3599cbb15bee1e7b984824d0c4e03061051afe8fe10fd8b85eb78628d28c678290dee5f04b3a6444eec7b8d1ebf4cb0174b1b8efbaf01fa2c74dc38969e9c42e249c7c5895c99e928ace0b65f9a54e610d20ae42fd8c6a035e6001b3914cd8a5f0175a68a226c318296c91ef37a6cb277928116dcbd02c40fe219aca834d1b2e94851408a6ebffde053fae2a285804456e47ac1599b9360123687214652efe84705e7758785a854b2dafee08736e0135f2ee20473dc8a40eec54feac0d7bf68a59b231104a2998a0904d9e17c3ff679d4f3beafde8b910cf7852c96b9ae2fbc2f9e1604eef8ff0e36f7b06b42cf40fc911b487bba686dd11103f7a506beebce25b9525596c00a160be77db9635647a3d810af447473ebf1617d1cc84b7a48274513c5ce05c2ff9ed435fb2b205b716ba5ed14bd3ba8942823501323bfba2a317c511be1ff4e40a14e01a43e651f0ca6029882bb98d66f0a6ebc9d660134c11bcf8e82dda9ee9e81e6f7498806cfa497d3248d5551388e0f48d098a0dc8f9c04366e680f6f55b62b0fc14a70bc627ba96966f30e5a39f63d3e3c0a1f1b55fa9733d9cfb5bed618ff90bd1d7be223c1ff492604f90e3b86303ec413ee608902bf529ed7895a82e584cb5e06ea66b3b746314537b3198e2e8ad39617ea8e7dca0d7165d3f16db733430aa8a61e2fff718e32197ea9e975a2988d18c16d0aaaf1891099e431e8e5658895b7a62dffac46cba0017200c6a23f0f070bb67db4bc30bb0848369075532a10a66535430e9c98a4e572bdfad0983300803ee8b82daaa99ddf0a7e8e405b64d0403e5d77feae23a6885922b1f23e644444bdba00ec03cf5965d50ac71b38a2d2c042443242d9ba60150d8c8794bcb82cb0402b4b10284ca3cf5e639d627487b102b26ea2f20b6b0803cccc33844a69c676e337b09c6facd1ed31826568c00c8ec1d5c806bd89ef10d386488f254b0386488bdb8463b012881083d3b81ad40fb79d052c5f38d911c9999318cabf8728425a9c44a483eefe9ba4413d4a783a39cbb46ed52a5871210e4035d191ca4e761308db389002b01343a9d6962d81e1a989e6ad551de1f4a3dfe1b432bc510e7bf72a066f1e9ba17ac0ff29959790abb760c063c4a3860419c3df0ad666ca590bcfa87b8e39d123f8ba925f0a92d399cb97b072026bf615a1228a40c3a96e0da86317d3e873abe3f5e4e2bd46acb3e6aa1834869796fe846163dde9bc1b49fb01e847eced7cb545d35188d75c5158169ab061133c89a5be68ab0605d6fd41e3c498c1d899b599aad159044f230a08d7a0bbcacae991c0bc6b8732c3974c0cb6b623aa0da4c397b47730fb280102779ea34c72efb29b86dde28a58f4ae725934bb73e304884842140887ec8421228181676dbbf35d634c786e635c8f5753674963c5db3b8f75305f4e8be5c6a84c662b3dc103bd47a9b2845f5ee30ddb4b9741715fd6383374f5d7a4e12255f85f19ac411959715957b9174bed35900d10a1e22ac66b73d91ce3d4c71671cef116a5a61f0986033a900b7cd3e6af0212753e1855653214d4312910fbaaeee2f5bd21d5907691651068427a83c61a20c3dd15b5ca8833a4af855f3ce98f17e3b8cce96400aa8f7767cd9bcff8374ec7818feaaec0ada9f2c02d601814fec4dd91e3ed098e29f8ea040d7a99f003b50615f8cfe22a14169b036deb5949cb3a83a123014039196c12c00cf2015af1fd7910268f5c3350b0e56806e7700101933f87a291af29a8bdf7f4833263ecce2d2382c3b4c9bbc352ed18b5e1eb7c2061f12af85aa2b83eeb35490c3e0e91ab83d15aad87fd605eed41442264c426ab80d7136ff660f86bb1873efa01ab5846c4e3af78e25754c1bd9a66d682e0cc55a125af968dbc932380bcf7aa69b721a98338da3ea1ecdf45abf6aee73c437cabb3c680b5ae5d7b4dcd95aa763a686836c61b52781fa0261a53580328a1fab6bf61bc37b733a30c798e66f0473bb8c0d830734b5434c2fabe28994a0170bbc97e12fbce47735d0922884ea687dc4c2610018558f2b9daff94847db795afe46f5694082e39af7b3c6a2adead2a7688deb48c0e482f63befc81859a151f1019f2f25a33ef4d7ceb79f22862a28fdf6680dc03c7c101f2858097c3e9320358c33a841662020f2f77ac0ba252322bb93906b9481911783ca6a42cb5c4a567526371021a8a6c9fa1af8ce5e11aa64303c88d87738c81ed456b373d610092e888b0d83ea5a87ffd1e5be52edcd302b61315a90f326c843a187f3b1b6b08fc3a2f250c24cc12cca099e0a84c5e22dc382f9366c2ca5fe07b07e3274ac0b94a555e06bf66ba1b9a497a6bec59cd0aa214ae9d41e2114cae98a806adbca8bf9c08de2f1a42cf3cb4eb55e9e8f75f5e8ffb7fec278ddea403393bc79120c71b1232b4ce06db3cdfd0e00797e9481c9140c83672ad83bb7d89af58d4baecebf06ad0607d26775a9dc29b28754d80590b43894991469ec4421088d03b50392d5010ee1d11c203a0469ed3254a3cc31998465657a1d6bafc638e4a52c8e9143c829c6e93c1cf38eef47da188c28ec9d405b3befdd71bbfb2d2bec2269d54fb5efca708b3f6f556abe711bb4baeec2fcc747e951789ae3fcf51f68efb47fb0bf854124346434a0acc40e0bdad7b20480ba18cd23f84cb241e38f8881330dfed72916eb96fbc79258bdb663350c9a13346557fb4d0ef063b80a56736aa6e47769221665ab17b1581674c1e0cb3863f80ea2715ce2d65534a4d643242bfabb4f2f4f83e27edab1c45e1ba5df51418bfa937a5932db51dac05a96d8b3f8c4cc570980db6ad89b86e51da7240d2828107c1e111656f5f0ba946b62bbb4d3408bd005875890ac041161dc3f50e88f8d32d3f276b61a5d60fed048147b4f5c85f174811eeb50dd3df8b35d9ec32f70f9f61204e21a7f8d826a198968c3cd790daa01457fa33ad001583026428e09c919a13f067430ff70ff6e1c34c0c762040b65df49987ff9af1d3aa9f9fd864b57eeeb1f75ab0a547154181cad43c1d09d14794e8045045793ccb10f3adaebd990e33739c467b7b493c54b7a97a9e278c2b65514aa1f90bb8bebd1112eec40b5d6daa258dbbab2a66f270eb4f8b3c9939c38114fb5ee927af2b4f74e1452ac0bb328902f30f838df84baacdf868a83f5cedb646b95e1b945c86887042085fdd2554d3fdbf6fd69800412f1972bfaef91a1f418858bca71ee52a00b332ccbbafbc44bf12aefa2df1bb722114b63c4814b8a1f30174c4542e8f59f0f8e7c484aa36d07590b4959432d3a17bb682382c38a7c69a26f67af76006f4cb356a636ea313166966726c01004563d21e3136bed7d97b49fda8d21144d63e36594bc87168bb8257343d34d19336e1c90da1f7abbbe8dc6fdabbb009f1759be14fb2bb6ecded1f89bd564c8285788af16205b294ca7fec476b33dc51f1811fea02157393b9635e284a4c6d436d2ea39689441fbaaad07b6047c92aa192b131888f00e21594ccca9456c7d51983f9ece02b25b5f229bbf3ed4e103e2117088cf85771f516eef21e5950866b05f640a28c5265f48bd063f8edbe11488140f977d9fca10f9f09aee586048083251582bcfbed165f61531cae0363fa04632640c619816754d6a84030c0c1adbb320ae6f9941dd11ae1b3554e5b26f492c6d6da2860c32db12c4f68cc304c52ceb1deed04b2ef196dfe7e2ab656ec7bc2f995002916921c8b3b51aff4378600856f4f8e022da9ef1912a1c211645d7a4f08ba6219f8468ca1fbd8617ecc341b7d490dcdb49309576f00a9742a049c6a9bef1b88b1404e0875b3b8f935708011b44085057fdf050fe708bfc36386bc389f8fefe869edc2abf018f927663037d76c043decfd861961defb4ec28d57dd67f51cd0717a667a2ccd80f96a006f07aae4f337f34c8badf5d95a44cc8cf76532a45fc2111f0ed33e02ab1a98bb0d7a17575f5f13940fc2ce0fe76be0633e2ccd279f1c0d933de72452dfc19a489969089936351cfac97bd502c69b329a35a71a91ef30bcb12d625535ae90d5a7dcd49e083d9a4f436ab485bf4166d1cbea53ba22eff15871771979a8611b34bd76bbe0a03862133b9ac536e447bda20dafafa70ee2e82117c5a301e5be8031a4bbf9793827a1dabd0d2ad856714769aaec7094a4dcafa769f5287d16d18a450fbcd9d4971d23d80aefc5b3ea8c0a58303060710fabc0773b754f1683a8abbbcb2e5c0d0abb4e2df5fa425806b38d7ff9f96a627b78ad0a2f79747d42e92a33db61a90cd80d0801a7b702df6044614eac37f77b612ece9f47a4ac8325a7cac54a722d464ea3ffaaabbede914947e13a59a6e5488c9a1c5eb2ee6b89012a0e30aedbe039897c22ea03769cc880f1d0200ba95a6e337e332d82cd272f5ef8c2e852f076b4c3a5fcaa95b9707a5cb689a8028f479925edcd40024d7e6d3b937763afed4e38d2556f1426b81222159f1b3793dd685bf73e6343bcd980dc79d9042417f3e119c1765548c9ebd2ecdc2745c316b5b7a7755228f032bc8e11442d6bf46a669a4a9c866a2884c2cf1db19bbb4481a3a294f0959dc281d73a7f811196174d296a1ed1a1afa315235f99830f658687f35d6e1211e3b25ed58413eb299202e52b26bd55c324ccddf1a9648dc589fc54146245c5a652574295971dfa6f962d6df53757c901045ffe2f7f650a1308daa69911d1d8a0c2ed5e20d7d068676b7d0c4e942d08134c06bbe05ffff23100a840434facbd8526e61c7f294e8bce2e9d472d996adba322ae5d7387e6444f31ae8d5c7e3604f0a61b3d0e635f2108bc49f3f0c5e24dce582b0dbfb40cfc8bfffaf0c0c3cd83291cae8ac7c7df31f61b8c2d94f89dcdca0379c11f59637346742f2b877b88be6f2e552ae3573a50dbe2079af0c25b5b399af4074f408e9ea57e6560f57ed698d40b0f4d99469a4b766ff087ab981c43d04cff8eac13709c84b87fdf0e7a63c4fab9b9c8a683e66790caf1bcf9b39cbae758483af48b7525414229daf836523ffe04a5c1bf4c527384826d910dd18647375c9983456d7bfa120da463ad875cfdc501a2dfdde910c3ce0e8b6a2e7db00c612f3f434d7baf1212ae41c4bf73c9e242d538dba45c4c1126d1f41b17ec398d592f4cbf7c88c49e55360d769c882564d21206fd0f6d66d5163053504d6a17fc95a7e3e4176cf8e102829d8b1b9061ccff25b4c744003c52ae2cfe85d8295d841577a0807fb3515beec912d4116ae20fff2ce3f98bcfd1165af76d28a343236d807e192a59050395ba7140fe2b1a90b3ea4976db411dbbf0ba13b6c12d2c446bf254d66c650544cd06ec361f9c05b6119b299ef61b0976f3179c83e5069fd5116367ea073216e3ab447b586bc3d68f18127dbade2a54a75130450e0da013a90e5cd0e19ce61f88d7d20c35ac78ca2a5d6d2550b096b3142d82094e92f5a5aabd826cf1df81cad80b2acfa800a370d4e0d9931e40901c9e7c13aef046aa4ba2728da74659868da9998e2a44c3e20d532a228367cb4b46af7d80217e6a2b4a97e7315f9680eea4bd08370a621aa7aed83f24b1642a76136aa54425036af3208c1621c05843e049dea603c1e7a87ae4a3f6063c2252802b8d0f774ec25d24ab50999001fdad2a5fb0aa359478f06b9abd9e7644262f48ff5a38734249c79afb866f3043d7a1e2c465447ef20053c85ed5f86cdddba55bfd8c3a4984443afcedc211d86a1e9b57e0d7c6be6a58c2a99a7ad33e98b22e27ca677a621420a6d90ce4d69420d000ddc6cb4d80b3aa0fc9259ba2616dc7468ee8f8e0a1a18044def09e153efb037a9c98a9546100ca77e8c93be081cd97a9451e289311b33c945763210ff990e6cb4532c3992cfbd34ebe1f0d4be3891a60375e15e090070f8bb0f0a2718fd243c1d07ea6b485c2286c5e85d73abb3ccbeb8b90f3ad7378ee97b143de9f81b99afa93b596f58b32e99755d6aad4abc8e19f432263200fadd5fc5825c001cf618db5e883bd8912cff807dc9b133659a44cc9410a17ec68e090dbc0905d640ca13f0b1338fd903b6fad61159e93a0b46a5fe1484aa0033d7b5f44b00ae5bb08f62c1bc0b0ef750b61b814874ab100fd212d1418725cea098b0e4fd5d507c81d4fca9e250466cc811f6e5104c96b13f1facb1f1c4819691e45e52128aec9e942db6253eb0748eb7dccc2ea91f729f1ec4c1a9dc4368075893edc31047b317a92086c9bc293d0930e3ca4ea9b9e06653bfaa9c82517fa8aef8523473bb3cd61167bcc92dc6ff86242be8667ea14c723a2d7ae62d45c1843cf35c745d26b51c02ceb6c64f601451071bc3cce36d3f99c721ff42349e68110282b76904a021253591f9d2246f110c505024568e396694489a0c67dea32611c07da520a71d38e884836e1c75c169271c3ae0a84b0e1d70d005a7ce38e9c8812e8e74e26c0727ba70a68b435d38d881932e1cebcaa1034e3a192a7c773f5a31df2213239d4856ced3deee222acaf2700d8f83c6330f0a7e4792b02dd2cbd8d9d44dae2f85f70ee2fb425c5d478a8ef11d811f6d0c860117fc70593416efee8ded101107bd1810579406893a801bc1affc0c7b772082f73827d01b1181e294b95212407be8d4535bf4627db8b9bc4f0393602d365bc68185f1b01315ce6ed93c096bac21a4cced5bc8eb23b054f4edb80edecab9df07fd9811134a541fbece8862e06f1eac50b2ea090e28aa750363a03197c971116bf58bc8015a676df1a4cabe13cb158364754231aeab5858b626b78cd0501de9d5f2728993a114230664a7a2c6dceb4ea8810274b26bdbc75bf02ad30424762187fef1d5649f1143d735f2845db0681522e3c7f34bf03249bbbed9b0394e6a7f397381bd9844a4456751d3187e02131f452e5b7ee02df48bc56dac29c98d58c480f66cf948558b1945ef334ece3e55b32d2c10053a265035acc222978398afe0e84ca04098889d020b0d33ce5260e992fe2c26a4140e2fe652f92d13b554e5b510042762819b1c2e41edb3c12fc330daa00451121a18c92e0cf773e1bc329f1c69adcf55ffbd257a4fd19616b38574f354ab9db08948dc8771b8047427018d33177d7881ded4f12ab9e35543ea1baae29a9ff9001d3e79f87b0bb4f21d1b8ac7cf02244fedb5c8c0be9d4a0cc9184504c0121f41421fb20f240a1d6d68c8f844ba6d8c601e183c0ba4b8c6a616968c1587c64b34b0c4c7930bcb2c18fc8a07e5efa6d8f8b710273237cb3feb254ce1d00d94118c1b6609d62eb603a85faa46e8420fc2ffe135eb186b72f2d293f466febda5a2ae62abd3d6d77088fc5c9b7ce4d64b4b41de4813172d730a7510cb40e5872832e01d24c283a0defe8c111eb67285451347172a5851e93763c9768cd53377e9e2d0964090d0e42a9e8d3b633ec7aa35b75935925b017f12a2d1d85a092d1c8dc101beb3c89fe3ad4da448b1f375d7e8b13820342ad0617b28a2354ddb40877ab025063599360e0fd60f2ec6de65826dee30a8da5d171e493e66e44f6df6c299cd460408128e85ad007050bcae54bebdf8e17aa0b615fd54d2ad649ac99dd177479c50aa104a802e3027d9108feee31d69b442caa400b42dada68aa1e1fd482771bf1e1dda31cf535aef00cdf9bfa9386b3ce88b68e40e2cf33e9e1dfc95f1843d4957c1f4f1c965479a15434371f5fae75b9cc54566c1dad540035988700cf4516f297b8b5bd052b7d8db348b22386826bcd84457e21313057f208450b93804a1cef661037bb79096b4c3c4420ac586a8e758643268c68b10f66e3ff654e33c848a22097fac60b42ef99143d5064edc303f8afc8a02b80f6a451e1989053e6424ec8660888e752da425af366c732c260388cdc943065a961b2f4d1243906206646d0df7bbca4c84d5cd7286ee0632e1ef76a9e65267bc51178ad92f71e28ba613733fcaefb3005bff4c1382766ef78b56baaceebf2b5c0097510c88074d25212e7de4ea106dc9fb4478fa755f7dd7155e3ba1c11206472a015beffa8b18bddebee45306b7801af04231ff1d5de25a53b61181883f8673db89c23048a6776c63805efc467e31cd4c4f3c8d2184d30eda2ae1a064eace0006d8b13c66963ebd1f7ed64c93981d38a36c1855014629eb0b88995febe15856aaa2096b11813983523513c261d170192e576a3ec78e8a3296c1803e9f01e24742d99e53734693f2116c2ccbc58f572caa48584f06a335e2c627128cfff2e33121493236b3b9c331d2f82560d0800d498428a3199cfcaf1ad151a336afd730585ea0b60a208db198d0e580040b6039619309c88590da1e22f16b4cec1b378e37643f6f18d64e023616a58adc95c33035ff1709a982d8d28c7ac6b9162c9111a40d7eb0dc7d8c39a087c97a6d607f3caa7edc8b82b769997e21a0e20cc85ba589428f821aba4422846197e20f269d0a0912bc098b3018831197a70e3e510e8906450e9369f5509a9529c3228c73d2b06f8bdc38f899725681307f82bff0fa0f1361f93b43dedb32892ed335c342bb57c520d6c6d060dd399197bb5542097f873f245944e7bcace86cd86cf3ccc798583f18cc7119a099302c9a32c9f15bd7f32070a9da193d92a15a7ffd8865727d2cb37b52849bf84702c7102708757689f4a92935b0619c68216986925df826d7a397de6b86b37e117f4aa6a25ed862ab57f34fcd7a49b266ca724eb736bc050b54606b8711a22ab70a01134cd723534c7324a86f82e818f45a7507780366d2c40ba030c04482cfc1dd7c5e54cafa2a6d2ee04d2c20cf6d24d9c59163bebc21ba58e0ab479363e32a5b3be3e5ec59b4dadf0e02d9d8547a65c7862e33149d6a20d96289acf048e848a732d32dc038fb1f5679f54d95952fafc84b775088e2e2bc7c123696290a7a604bb02956c09a697a0c1623b15f874be73763622ea210c5b462ea4c8b80e913878992bd5b7fd9ac386c6e3f35bcbd13fb1a80d773244e7ea50004762fe67e7d899a98da708a473b367c79be95817e520b2652ffea329fa8a61d66c2a45b9dde648ebd9cf638fcd85132071fcb47a167c3740049c85b49532421573f6a79280a719ee9e187448babe9ac30c9eab9889609dacb58add92d702a363750d9a3ac3294de1963d56434eb026eb5bbb60a4e4d01e3443bec6dd02ae66dc209512264c811caaba55d47f55dc6710088094a6bd9dd450fb4b3a452992c1c570ecc693a8817d904d6b9a15cf7e2ee62d98475a8fa8ae17ac9f2dbfdbeaf494c7eedc7e9b7124aaf2604b83ce124a03d88500adcaacd69f533135270164837b579693edccf881822128041969212f9a4f4708adec033865c2421e343d3960fa471246d253e633eab10460a7285f30c0d3bb4d6fbc7765fb379a9ccf1ef0bbb460770d206627ac59369589b4117eaaad67070aa607409776c05073040e482f44cbb125ddb6185a4b5a5a25268a6d20ee754ae8234a180ab6b1e0d2dfafc17f15059af347baa464c568cc44f3578cb251f92a3a50c68437aceee434a1ed5accc22b86a8655cbccf2b32d77c92f251f607ccd520ec9f0da5b8286d0c1b967b587c05be80c17bb161f2f60653f5ef27bd667f5098080ec553850eef6c77be8368c489432c0e0c954c2367fe1021bf9d170339461d7307c61d37b79384413f398e247a956401b3534b821eeb574ae886a4e66a94156b0872bc2e0820c0ec43ab43ea0c786f5e5023323fcd46fc049e914834d1c0aaadf702ca544a07834dc8198c5e283bf99637b6ccaa77dde5adc8d96d178957c8536ef3e9fa4d3ca3bc69161000f2833334a4c2c905d0116d665b9355580aab51958d7de6c55acd4bc30cf28cd60737ff7b7a053d70227208c109bdc609a6c8b08afaa5184b5bdb40a4fba0ef962848162b5a2e0068a9e15a427f4ea2064c83991bf29766f5782ca7b4d19d55ddf981e4b729d5df50dd664eb660e146f2be0be476038120e2ea45ce0267a53f666e897ff54af84d921e1ede6a8581f19757344f0e0c085afab7aa1122470c69192614eeef42e3b8a35ddd0dca40c19143170d5fc008d76323af7930a91d51b0a961e355a55bf8122bec63ea0c051561d0f4d585759a1ce3e618f80878169ffd05411bdeb318ee5cb1811afec9da8ad57a349f982639eeab87774c6d3fbf170270bcdf20e44a1a2d7fc784061f9d7f252f7d3df8f64c91717d7be8f2e9911c1ce10d53de5e9b9e23801a68b674793505008b0cc0bc3453145c65fea0de59897ea5f93b7dff98043f80e7842c9e0ab6132d89fd862733040526e7dbc1658857bf248b97f9a03a3a788b9d43aeb76ac49508b331d4115a1e91dd230b8eca25fb5412d432dd292d1f35d81325dac07a3820621376bac23660307ad6e7101600e1ab14319c55071dd18647f608bc3697119fa619207d6d6a52e75a03a73f69d31acde0591b4068a0b8f092b9bb5637c136ac406342e2025439e09a69d0a94d8b4d8b0d9445805657b160f3f03b8b9a40663c6109a6f222ae546436b4410f624fe80176bb360f3ab808051aabbfb38ce675ac0ec91623951d393fea00455633d4a3dffb806a7e9805fa90fab6d61eb704bf7e6689ef3ba08a1dc9944a18750530238e7546814172a651d2c7a8ce4ddbbc11b3e306db0dd5a8bf8bbf319fa7ae7989703af41086912dafd411762fb9f6e8deaacb777810d64437f28284ae6622b5b518035da643aa481e5c7e6866a976cc16831ee6ad65c87d3d3c595531ff8b3c2545c4228b0091a07b158f675b8622f9647d073b90ba01db36668a452816e63d5077b342c5c5dcf4d4875074bdd96f8e513d6d7b85bca5e0b21212f1ecfe906fbbe83b52e032d6baaae60322c0988a7f2366529416edcc5abd2a3d8c2ceb1bf13ac6bdc49717d6ec28d93921a2359437564699142d87f3a88457b015fc491bf1647b38227a5d4e1427150e9311ed993444838d7d354c3d38ea8e525c18b09db2d4860a2a4064d94b8e46812a5e8e8b75a07b25ee363e56e82855edd322191c5d81caf72c4b103bac8e5fe139c55d84326608d2b5c2b6e3327250976efedaa8b4af3f479f1c7ef7bbb0b63f00ffb7178f29c0c4164b854cca55fff60bed7fb4dedf55315533667cd847a2577d1fd9fffbc60e76f1d125ed64f22358363068dd9fab4d3ea33b8f04da26a1206e505d48af645dc3b217abcd6f8f54961c6d7909fa3182cb14ef493a0840c90c9e05800d2f559c41b15435e44d85e41008b439d9165f14cfdc8036409a3723c9524421ae23969ae0bc619a188723808311ddc783ce8381f8a6b6a957b396cddeac23ad157797ba4c995e7570c93bf5ff160ab16ac25686072f2cee14d1a40a0cd870788dbd743e44d076730b10308104639459938622ac73e243fc6442eee0973d6bde04014963ec4180e9800248d7b9bc3d378040bcd90dc9e2f581fa0299281431037bd06e45fe0f094e744b314122bd8b63c74ed3def6b531884a6475c09cba3f84c27edaa883d47b1a8b3f0f8900fb4039ad8050c12a2f79d6a600df11ce34e4b8597c49c19b8d245d5570a3d89d1d98700819e7980d2771c4ed7c9d74d7c8512db42b557d82a9fd703297c85817edc3470a69e95ebf9e6be950e2669d9f01610807ca590496fd0370c761d5c3e9fa21a378dbe099e22f382e779a0e51fc089d75dc2f92ebe3735ad1b05d9843f160ae42a3548ae15f5eca457b0e659f90177f503cb81cc1e01466d6764ccf636cd181980a68faf99b2337dbf2a0c6d53d535b6dbbcbf8be549410bc41cbdad89381b64ee3e8f9ad8257a17391a9a476857e94b3029bd2d80062d4df106dfc6e2537f131176ac6ac305c686af0d97575fffdf0525eb5af4bd46f7a06289666ff6d02048ecd4c353167f1d2f767f0e739d1254c116e5f739520e6c133c53543658a3ca292ce8d521b0c37552f75e5c711a4d4bd000318244a69f1b8c8bc5ca5b67deaa9a6c0e14b1db2cd017c3e90665f54b6d9a8c03e7737126329ffb166ca31109f4e1e319ea16b657362c9950755d31d7084d2010b1ed007c9d3e3679b8088b2e11a868cdcee8e4fc721a559f1f6032a07a8165fd874604a3227821e0853d21f0104edd08a5eb180e4a20fe2985ced1ce5185c6d3c4ea0918af677868645d7915a4b0453e9c10bdf65cddae20be1bd9e1f94d95b666d901594025211f2a0acb063032dea0a04ecd50df10a945ce846a19e5c954b82952f70b78c9e0ba3a2d096a7dc9124a8dc1b738976b09c710ba65f505429e521d2bc813c865215782506ba51072321b1aeb02f083ceec455005e94439809c4c440540efea96261d8ee18028df768e51da208904ceb547fbda1f91b5cdb85295d5fe15877302fe6077fa477c29193ec397ebd9880dff58ec82c5a086c484c6eb72f7a9ba25e3b7b16324c1fb202a3ee4de1308417d79cb20ca1b4140a601dd246900e4c8c07c57808d2a8567b2ada679827c24099ce9085e0637de75503d7476e2964b2e2841e26c9fd35bdf1a5124b4d139e298fff7bc26d74e882c2fb3d203346f2bbc69a86c2a481cc0b1dc4da4d99ceb3adaf68af0b57c70fc2e4af2bb2e54c7c52633711c7d57a9f2ac4121ccf6e95827c963bc0ac54ca73c0fb1289412283ddd2b78916eedcd89771084f11c229064144cfbee5088f3195fd3c4e1610b2aa99812893228c3ce4a1e8d25d1726e5664481f8dfba85ad3985b67b03cb41f46306a46971553c1328cee96a4b395802be10346320214951e3f373c81e5147584803c0aae8b465f56a3a2a0580204fb9dc2fc02db5f1decd77b28e0c4fa269b8a7a01fc1e2c3a50d55ddb862502a4378a36271d4ed626a6506ae4582c8725b090e4cee8dbec1f19db2fe7852fbce2a917aaca6ebb30a2a22a10ba4f903c8135214f1d82734f5909500f7f52429f6d9f5052934aa08363ac2c7b5c5a2cb94b2cf43153e93f41ec61cefd3f38c832f5ed0a70d7d17f541d84f7de8fee5da71b1872e1209498f13812a3d086b970bb406f8e313f4fcba513875c43138ac24298ebb55e0d466277ebca71ed0bc8f0ab857021b8aef3233f0d3abdedba33e65ff2d822d9966cf3132f11056d4ad94f66281edacd79aa255448f7189db57bbc9eea28e235dc3197fd3635fe30f91347425dceb29f38a405a1d52342ee647e3ad9133d2caa5d71e70764569085933486b3c3efb20587a508f449c2fe9b012da6b2d02109fb4f063ac49b88c609534eef20803c3b7ecf4e484f28bf3b994b86effd1e8de8031e21f36012d3bab71af740f9d3b2639a910b10815ce767613c1e5bfb7484f53bf4fb1e3c00539bd8991a61848ab45b3bc104509598e4d1c13075d8fc8bf4091e195dc18d8102c2688ee833eeb2352ee98fa2a683f011ab7b3f4790f1c70a6f51cad06c4a7ede8007c9f90413c8189d2cc95c449bd45f78832f992851580f8303b53e53558d849001c64522dfbdabf06285feb955e96e1bbbe410a22d842b6780ee436f10b012876688d4d6d5edede6bd3dbaf608ff8a3e4f64df9126237d26bf41935ce30fd7354a904d9c215993d6600d3abcd618c48e04a16e6cc73f14a986c1760df2fc244f6e5d6c225464e36b0f40057c8d86fbf4ec7e5bdfbcae456eafc426785eba8269e58b80eb2952f989c3305065d93906542347783666b83130758040df5ebd6d4c4cdeb316521ff514cdb4cf8a983cfc44d76f402f58d66c9aff8e7e47b77eb986951402303b70e80972d0ae2abe3f29a8a3c5f9809bd133c1f397039dd5b731bec64d5c26605dbbd3f3f781b2fc9702baf2bbd6470ac796533b99bd5a7e9e69b2187a3d6abd8b015b6f620c1ffe7fa5ef9a609cbc3deac1b33d4a6c0e5622c37c75899b1693a997db7245e3f1b5c109728c1e3dc309fce937fef442b9c7184290fced4944dd382ace819f561bd30366358426c1a45b122703be504ae6edb046b68027beb6e57d42d086df6cce1948a862e5cf60107e0f390f1ad57eff9ccb468c0a91cb6572c57a5687e12dee6ef9db98b32f0f73b5fd9c2188eeb1395a6f150c2f31dd25caadf8b083d87d607eab873f2a58f0f4624e365db6d32a474a924774c70aa4b20fbbe6f1339f18c49a8b3f09cde00a838991cc2eb2600c4c1961f80da110a5cfcf4dd9895412d05b21b23604c59ff51386c32224be557ed556b2aea2bfbb538ef76427bd6279f0284dbab9af7386f16ff47be6002fa98cdb84a7561e8c9b770f4bbc01637f9226ca1aa2a1d697397ba5bd66f0a678f1be8d2a8f401778033806b6035cd292b4caec3e3fc43fb3846993d1e77cd3989e825b4f23651667c7504ba0c60644f2652a8db08019d162b178d028bea10c137b8b8ec4a6245a07a26518107e5ceb1a73c94ca3ba5a96c3d34f30c2656cc47e550f1012c2c86b363d74e0e82cd04d44628c4ccac717a4e6a2c05d462a0a3b79944425c92e7e2992ab58d7955e3930dc976a74992812553c1654ea2045a573a4124992236b18087c5832902ba094234efbebc50e9475d3f6c14722f06bf7aa3c8b363d31768ac73953705e6b4ff44713f147291b8a85acebbd84102a1ff619c2da2d2d526aa52fa1a41490dbe134a97fc113c078fd378491730727840e44022d22e715f5f3018580f5e54c9d0a6ec9e0f94fd42c03b1fb5a199a8124305e7656fa2f969c810c65068f3c76c8b369b746f1825b08f277fa779ad055bd5712ca870b906412bb8032dbebf76a9854e54880c7b95003acd43999c60f9cdc7ddb2e60dedb38f07230ec27590c730df33618ac0d28a4012188091cc43f87bdd9e8dfa21610d51012455e235a4760bf94c4d096599096b93f2e0d428c117a3d73306b6034825d084a4c3d1dd625f881d191c2ca327d7c4b4405924659cadb7dc18700d8647c0d3ac241c9b22c094c8d9be099cd56f8cffd42455b87ba6a3de492b4f0f320c90ba88270b08854aa8a9e16f67cc86ac475648a22bdbc6ee90031f512093bbae46c53e8de88d974912add9ee73aa619c100cb96aae6dda1947074b1bdf3eb392e67fbaa6d674729d24868f423b3c337df0468a92255310fbfcdc225f3fe826fe6c39f0c3570dc4800caccf2c42378031d2707545fce0df147bf1f577746bca5fa0704d3e814b2c49831ad5c744ffe904f07cb82ab7f2e0a722b4afb6a1a2bfbb3537e5c2a363520723e1011b73a16fd7782e1d1bcad90e3f874e2a08457d0bcec40a0777593c078293b90411b368670d637f530a3758eb0635b4703bd0eca6f3f08e6f709928610a6669775f188f85cf9749d824990951d5d7ebb86548eaaaeb3d8dfb6472a0a7063e876c9c9689798ecbe36622f0a5f7fca09a2295a5a1e4e6fd128d478a890dd536fee0fc448863bf9f54868b6935f3ddf0ac5d7f18e88756c623d6dffd12aa95205f2b5143f7975efa32a3a3b99bd78600fe9e28296b79c25afc2b49e439c270303a446239f34f1b39adb6c0500afeb5d86aae24d45a319f969fb8e4da2f032e76a1af6091c88e41a8eff3f3c0e1223128bff1c9f0b5ba54df4d66685096f201b3cff91a5295e6fba04f2783e60315d44a06021ef15d4b593a5b1eebe7245e6d34c4c87a35774f2212f00285273455f1cba8737ea894898b75a5d8831a7ccb987ea3e30306ff9632450bd5f95bfc3eaf30f2293043feebf169b9edd103332a85c6fc782d3f0f53a9cc0d0b7d48055458ddf36833bdc968b3fa071252d4087a38f06ab25ad305cf193581aba348a9e120c001f25ac6a463a943a0812923e8fa1852240d4104c27b87ceae373a1c39dc3bff25621297caddc06397148084e080262e04035b81102c40cddf0c00458a444a4f134e86da6b10c4afc372b57061d7dd93019e2a78871be61067941a49ac8f5b7be5a061c793188ce5958d2f3478a2d99f0f70308681fdd98f982835ef4ea54c880650e0f7b4659ce458f884678ee2cedd273565f499ea4675b37babc6a30b1b75018e7ba7b2947604ae878cb059ed6bf04cbe9a73b977587bf0094cf9a46db6a56429a917a361886d45a24e3f27d318d0fabe436623c122eb9e1f62d670e61162d24444e7b1ea2184dadfcae40fea67fc824359d726fae7e4e84484dc6b9848fb0b285111601499aa698da2699250127b176226a791e5980ade1d4007ef4aa5738763fb78d86a38f918943d867dbbe4de99017032900b75c2a64f5fe5523bb7fa5c52f56378b69c67b431e3db21298ce428737fcb31911c5011d154ae862f24eb32d4e23ba00c6c714b1270e8c293727a1ba9063483fcddf09a7dc8aa2cefd4dbd1ee22aac7e7e4822b54c7f0f522e9280aef3c0280f7235e646a2fb644a43070855ed70ccc2f356e89e26f2b332a4d9cebc2fdb57840354e33904a0ac25a47bfb0ed5e2fb7e571bbc2a3cf539acd4dc0a956ed3561c2994c1a53943e387c18eada9520c302caf967a30447250e1384ba1da52b11b70c45c58fa85f37101321c03ae325f43e5948dc815d37946b67d0e86e1f249a20870af0b8ebfde632bfc39f391c04dcafb10baddbf2221170990322b628ca84d1c293ff4b9c9b10b386a2d522e81f51d7d9cd0fb0b34ac35b4b87b7291562e77e7521f689e16d6b46e0df8918a8ffe21745f9a1d6423755c8398a591e9594e55a82839718d8a39f169dc19ae8149fdd852b6ea0c3d4b666f55296e11328661e76a1041c2989c0e6252fe5eeb0e03574f867f078d5e30760f2acce3df26c40b75e7525d0e86f82a25a637b1316245d02bdc7d01e6e4a18c81ce678c6bd1a4afcffe9f7aa001886b218e4b9e0fbc6c5e41a983a6ac4649bf3e15ec0dd539399875de9106af0984566ab780a9b674e56ce41c747893aa127e01e4da7301c56dd6fe284a1b7652c49230a6b2d2c5e4d12d06df661e9090bdb07b9d492c338b0f0b45c9a02c32cbe7c44d9a464ccc8625032e2de989163d1e292c6d0c3c49f71aa1def0aaac15219d43001eb3faab9eff34c21cc0fd6367584631fb6073c8d4e3ac6eba91a601f9273cabc7b8f6a922197b8b2596cf5d41daa736c2bf5f0830ce7027ae050f0d48252230887f0a5d9cd3837a2bf8cb042b101e8837cb098ea289a8b8c0a96bdbaf3e4621c1e44d43498bde1cfde77002cea36ae67035ea139e1fb7cf324c323d0a0c5ac7c9cac54cdc34c89644cef2c34d1a685f6949bcf3b11847dbd8403ff935f0f41a2f7119305cbe37342f3a1c32e92b18fe1f41c8b095615f6c12c5ab535001514fd6357fef81bb236f1430a07445b9dc19f0d0b4da4a90acae6644daeec81e189d3bd407edf5fe5b037e46bfb8e5a36d110a2bff2100901f5bbc7b2f7041cfd809803b006bba7fc5527ced5d640419135f285b2b460be986c702b99cd2a7838f41465cc08181044222b5e44d1e22ff94c0790407bb7ea3fa7204cf522a7214706629f38a6da3ce99135a5b56ee32146098662f480de750974c94e7d5ad9c290bf876a286757f8492a5da9122a1c11430a58eac8169c4d39040f45cb32b2a9d806605ac564d8c9bc7afc7e51681a3107d774789939d66ce254413cdc700338505595b8034ee72d59c29020032d601f38eda2b0bf1547498b9595cca8e8dad09ccf9cc576268dd3d88a11f2d4553750912637754d6bf7891fc126aac068262ae20863defe9f59a430a2d79d39d9cd6c9696be8524898e6bda0c49a8ad60ddb709a3926209186693dc0ef0e2f94ec8d144a323ec704cb4e1a69ac5f781c9aafdf318a17d2f4ed1acf7499e6e62b6ff28f70c3ec1d0ea63f914be60365065a813f30fe2b5ac8c9432be30f722c9c47ec6a0afd755aef4f85f7d3a6275a6c49db304fb730676e51fe458f677a6e6718706c225b34018afd1ac878e0f4128a3280de22961af4e53308b6fcb1435db2df3e57a56bf1452fceed46e1e58f9951770814bcb79eadf3623bcc8050759ef922a9e491b2aa0faf9dcf1b8ec02cf8d78fd2e3870776551bd89d8bb1493cfc74eed3ffb70f3345e82372427a54901119e4942e436c78170f02482aba7d16c85ca7c29ee76d4d82b622656330d6451a45f8daa12f1f19042140074ae480f15c7b4ec2247df0ebc3414c2d749b0df40751c5c59674d5449ac435165a39bfc144aab64f9723dcf1b73c6425d93d31c5fe8def18f6b4d93e5292ab12938f5b5ae80fb054e62675ca45f69378621b6b889383fc9855e234cf81d3990b85c07e09e7f5567c81ceece66fb39a68ab86e30900d7fc4c8de87d133a3e2f09c9bf342e302dcc26d90642f11474ae380cc4f4420f1216726de3b1a2fa5b814e41621e79f61594d5b970769b8169e825ffd33f338ee1c0755c8f1aa88723aeaa14f07c9bc484cfae924e0976a4fbd0e0fa31ac248cd24f6e4bf0d903dc72e6d435971f3f348a9d79ade158c17eaaf626f946be9bcee8fd30c263ecd4be5cbd5767f87abd32b9dc8e3f1a2d2766a0c54566e60108c3cf4f3294671640f1679986b4be3680e101b4c01a9803f4796233986f3fbcb459720b166cc6a08a71615176481499b231f65864a55a4d3d7a6c332134b8a13bde01d31cec9828da817823b8e6e0791c7d5abf7ab1f08bd596ddc3d9151e094b7797e1cbbe1fe4b195ae957fa83586ef3e93a5a8442075a25aa8e1d04bebd80615c5fdfca99ac9efd0b08db9ac92c0c00a8c811d6f5446546309fc7f6f01099ce24a341cc8e2ab3312ecfdfe9bb3c95d924288cb130d3f7ee0473c4c47a2b43b6aec625e78615ea6e817d41a91d65b54b4c0070ab312ec326a838c2f99fe93562c641d09475e7039f4a807995bbe8b129b5f4acb340ee74d4c6a38b944ac8234bfb67fef3985db1ac967c02530b0740d654bb47a6c316193aa8c0117420da4e6c9a82366d982c729cfe450cedb6831490ced7066b27ff53595feccdacac6bff1c45a59c463eed08938797083a62ff58eb80c4a5b9437003a3ca039151e7ac7294cb2c8b0a9bbb42a82fab7dd892cf65f91dbb9c32ce3a4c0f810f6c768fd55829b7069fbdc95abb75036ad8f33faf29b50133a688caeada9d5d3b3a2fe012ad898ec222dfb73a6a504c3d54880fc934eb668c1c5c4f1171ddcb1eb039833c3788077a116a1550a3c7df7b0624509df4f62b008f42716134b58e432007717cd7ce7369a4db881aa348ad5a1322213226935a3e89e5ae7d20f38e2ccf108b1485a84f4822646990b206eea1dbbe711c8e5edd3fd3b0761a4c8762cbb5f6c20e03a5af6e006698845bb7153a23e8394d17b695e6c91be652721db59ba1ab0b9aa79872016f0250815823f6db195161756e62d8f050284a71a04f7f15413c8062407b2813bece3214819e2bd1d72f500ad3e35801a402e31affa1afaa3ec78b18ea6ceb6db6b8c6aad8116c79d0d271bf9ba215e43a13ddce12123d7da434f236783c11ca95d9474d59947ad6a1dcdb2d911a442e5ca35f9c7e9a49528a5fb86524940f79194f3f769bda78019de284f7570f1a89a0e98866820834b00925ee55006109ac7fa2cb9149211b922b5e21fbbd723a2e0de1f641ed401b01bf1f3bf272c147448605d05205ae4e1ce0c998283a4e7892968a54e17580da67e2490631b75a6c7c944a5f1816bcdfdee1fd5491826edd020675a2ecd867f29f5af04330b5abac3cb36d7ac2b3742650570829fae19f642b11705142400be19e3ed85d13ce3d84f84b4ae6dafbbb959eb0e8fc6027b095849159555dcb7c3fe622ee66a977785c8026811ca378a3439864682400847231bcd2ba361c3d056d89833a4151c0b5e06a4559b9f345710b6e80eb4c2dcca5e5acfad111a0020b549a32698d7a98229cea96516ff0460a35f5bd7318c39489ddf04437c3da4502ac3c267a1ce8443de6ed2773e28d512c77b4aaccf0d81c3395c0faa70f4d2393b33b5ec4ce755defa353996850442536e969c50ed4a1fb624a3263c4712142ce426526e6f961f202dff88d608cb1db9aadd757430aa7b8fa6ee1b38f50615b61142340f229f942642b0c290a766e15787cc52601436ab2b1c46db7b5613c7687417a2d02f95490f34c400b9232a2be9cdc82d7175013838c316924b0477fdabdf8b8317cdfe879ec5744fc2bfe01d1794ca04880cd884ebc7d8bd4da5efdca46086f5938d1672f8a965d696490214f625f08f7602997c6b31c1100c11db0f06520525d33b39a0795beea0a5a997648c08ecffc3458657c6c152871f6494bc2e8e207737b02c15a6e600ed31e344fcc142a38c284e616e7b4573903766c2db281e101980b5ccf693a6488102db6fed34c768902659754945069cdd3f817ad25c90216f602a23a788d7b2c8a60369f4fe6c8b0e4192b75768b910099dbdb21a64b441d559a4912c0456e2071fc6eed0862a71826b15f9de5cd38275e3f88a2afebbf18be605bc0b804a3d88187a56099c7c7a932d2106e63b664fdcb603b20b6603c2f5c4c0caaa4f31ff87c346eb149bcbaff06bb9a66939a5dea02f18aa9e35d8308d9bcea8b24054fc621618230b2a066b0673614e60024f813124cc10220712deedd76c102552e98924513d1acdffd11a27230342581c86b16b874ddecca9451e09da6fc10c82d1daddcc1937cb86d1a200a5856ebf796e209ef0c4ede08dac8f8fc52a1ced7afa853843753aac5d45ab2a94fb265f2ad5f0d0dc1ff452b51b7f9d41e8405c20900e299b5115ca84236552119c860a047706c3daf6d88a185db8565ddbf2a8f4b9c5dae30970d413dfcb8eb56da800c9325a1a4dac0d9c6204a55d1f8c1cd285d30bca7f62e4d796b281d2703d98a080efd2950ef483490778b6361c3189d101cba264509ee456e5f2008255977fe506181f57cdd565a277236b3161c30d3b3ce5239fbd2bb201d1a4b51e8a285dab55e11284c192e66640a0dbdbb6d0e386401485eb7f5402acfa352d1e189650c44d4163e0de741b9ce0fe3e50929208fb8cb1c8ad6ac9c84eaadbe4fc85f336b680d8b4672510db360d897ec12dd289339f098bf2c185f4a4a27d16a711e16f27c577dc05539fd6ed85e1e774d8a3c4dbfea4547c83479056520b2dc24dedf643b3195183e00c122029cf09268bf9453d5cdf527e71893d32884adccbce957bcd9640bdf86d715cdd5e74cea529e83787730ea6a6dabccc75e6309a57049ccae63f725ec57161d262ad01b1fbb408db6b4a9189b0ed64c374c2953e5dadab14dc256df146fee23aa1c8c818833f018950f5103d2493a731e642b2e7cf9157c4e62da460448df35f0f26afd61947b182a42882c8aa4c400203426302b943a74e776c3a8c5fcc0357a94608a97a835b762775b5f67ded67cb1234562d20dce64b7904026739b91a64655848c3e8ac34876fa10278b79189fd668a58cba7b804a911b011be831ac3eccefb144987a1e609f75c27c2f83b4c3ed7835921150abc885a5079c7b16bc0641111832dbaa09e1484106a93b1e1b81d58daea37928016de792fe20f7a4c7b3c9a8474cdcb2c63592e20e1b3df3cefb7a5543b85c294de1520d920d795d6bd444ff6509c2f2bcb7103567136688992eb39861956ca1e033d18ed0a0d978c760fccf722098a8bfa9e8d3186123f8e41dffb4cf700c33ac53f954369571abda491f2985dab1c704b5a6ada56723832e0348b71006aa136af004e22e5140d1035517a05d1c4e89696d18980cc061cd2cd52b400dd1c1acf23f197ec354e26c3b8e1c664fed7481b35e827e93f8014047414b2c39a58a7abe2586b5c0efaf77b292066078278057175a448ec9c04ba7baa9ec3593879eda05f8802ef6ffaceba6d086d219c039fd39147483639d39481ec6178adb81a17140e21ace6cdc675fa1148af936fae852a32abb4548e47f2c5a65b3e680a86f527ddf43cd6534620f6a01ae9c86622ce469e98430a036b6eae9b5e9b21ec81c870789b7f5f038e7a5b795c5a17a705443f1404125a5e9b5f787aacc7ac5983f9ef3cb6f7306e51b6125b28eb034a4dcda79025d18c5363d9ac7f6421a8c086a1e3842038689e8456b056fca74131836455a1f1062927d1cd7d4eb8314ecaaec559f6a938f5a49573d6e8f8df130fbbd2bc76537e9cc0b5893ee01f3a4fa0651ae6780439c23911ecf7988d9dfe9e11ed57067a80a95a67de7beb1d863ca2c545392aec0f280fde9994bc19da953911fd351c1ec62b6469bc27897e371a493d45ad343c0b26db47d91f11ada2e83f43b6a066ebd09afc973a53af8e97b65658d5a903a48b6eb66190630b5ccbfba0653ebb8e442e253a70b37373c56d0cdf60e1e34005a27a16511711cde786062552ac4c8c9e0746211275f652f01ad0d49aea33708db1579d31f2208c34c4aa62b4d4c87ec53080ed41b0bbef2a2aa0013184a6c76bdc48a4d771cad0f6eee948acbcc66c900fbd45c239125c241128068cc49e47534c5e2fc13c9c8f5014b5ec05c8287cc8c96820921648c8389f49fafcfb3feb7eea30cb336ac8cde74746a32370b322daaa9648f3bcd5b71193647471580928ee9f4aef623d767d415a9e04e72832dbd5e1dc554c3c3edf2e357a5b870427df5937cc8c40d5870779d57af6010573773119af6ce3fbaf9f584deb56e0ccd1b032f81197f3e2b2074ff004152a9cbf5d48f55f8af4431f1771c61221e60f01efaa06284b8f48c19eda3b66d872fc61c20b8c50ad5e869bd19a724c46db0b9ff87b59f326b91689498b0439a9583eace2c65d15533a8b857e4b281c8ddaa999cc8309cb573bf830fe86def6328378b0853cb60400b94f66ccaac5db9c18b9dfa0af7da3c711e3dd27f91981b4aad7ec6df71d472a5adddd80b8ad816703d95e7a6bc56800772110a18ada37d320db75163ebc80bdcda7c541ad55c4140b33061ab72861654ddba1614adae42216869a6a8adc73c6b4d162a823b1c8ed818520d30a010da0bcf1e518a56ab1c7ad96a497d5d556aa064614fd5c2fe3bc568978753d33ff9184a39533d099c0a5bb76846692e2d3a7453a7d203df2a1e648dd63f64ec37e78a8b072cf80e742c5bfe6577ed941c6e1782ad5f52e1a2952bd98f0277d4b9161e8029eafba4563630332adaf2bec51bd43948df7c0e17796901130f6435d618a6c92c73dd17b032c0ef475c6eee063f5756b7346befe166124a524d4c7a9269dd5a8cdca8f26b1f595059d7f0dbdf728181b6c493f8a14ec97e487cea1ee6cd4323780767160893840f189051a60b51dec87e1690e44523acc2dcd4b7008f386d04929061d7591448327177d5807686af77a83f0131272d69a619efe823f18eec0d864fde2a08f13ec0900a82e20ce869ef06f1a60835aaf9cb225c9c40bc26cb375b88016d4c367b289adf648f64f6e4c909cebd16cbf66bb5c2ec87f76026c87539abd6dbac47a6c4054a39b4289ebdf1f8b818502d13b3eeefe075be0ccd01d2a96b186db80d79358c754b14ed1665f729607e6a429ea3e90d32289d2037a820e3fd10bf42838f551253d99f06e267dfa3210b379e74381128116fc8684b6fb9a0f5944c502a684147dfbe74f83407cd247e337ba131141095e54685a442f3a27ac67bcc32a79d87024b9bd6c739b464ca4e1b985fd088208798eb04bd852eaea0e3ae0c562191c1b86a2c386987b448105b498e64ada3903716836f17918509f24fa25d1f11674e0d4f53a391111058fe3041b958cc4a373c82b95e18a41ea9a7db0447c8bc12b917f575cf1aefedc5dc5207755cb76f56ced2a80da0e38bbc2c301c50855d8153fc4f145420eed62c7ca3366b142ba25e83e7f83083ac49bed7bb31db995b8171dc43a65f09020dede520bd5a7cd46738a8ae9ea4a39dbef6e7fa18d1a9f8a06d6eb1f613d290ae095f48fdb0cbb3127d8b9786dadcc0afcdf1f7912d7b40c36f4737c224b8dfd5f3633915975c0215b93be86a8493996109af3fa0ad71a1489c3c11aba2e86e240887c2019a259526c243f57e18d0d54ffe9c7e84fded5510b1e46b68df2e4f6680f16da790f9bc3f0bc13635e2e9c0c41cd71645e05d19bf0dba49079d7523563440be52fbe432354360f1a1d9085852dce8c67bff2d565f24f0a91d57b5d0fcbe40aa9398e78697217cb45f6963ded43d6086f621967ce6ee8c4659ca07801be648da271749027c332700c0e2560266f4ad94778041028247a8a11a0cb84e1e7f29a3153322b33efcffdf5573f8d7732d6c803c352318b3cf4d5dc8f1b716c4cfd23561a52fa15e26f0afe85407d105bf94926a3113ac997cbd266bf19c15d72a6b3418a0ade70e024d161871e04970fd7715d550607e35ecb6360eef5f597aeace6ef2508cdb9d7488d9a9e5e696aac1bc120773a8c6358a2e1d3459c6433042f7e5028acdcb89129eadc1581d3a2e2d3f84b154d5a3ee469f5dff2a6d158bd2143e55a61d0db50b1c870de5c3c34b19c3a380e28258fb48a08ccb458250e56b8d4897a014289ee36aeeea11eaaae6e9c2644d5050e86fac327412e6183cb45b904c4133b35c1273b35f52393ce28f83f3f522db290ba1b0f6f79d9ca8f5a4091dbc2e8c7e25be09a0be13b3e5d28dc3053fa2a8230b0f04fd155c106816b35ae2185014bcc10e0a990427f96bf1fe0477c6c47c481cb20e5e147ec7fab3c58e321aa8932b50df59fd2d0baae5400c9b527abe088fc55bf76eb4575ac4596c52f400359e65ded269f961b0442b1ab0c2ecce8d9ad079891cc0466c79ccc3c3a323592380cfcabe6de77868414d846e59aa99ea72500a8bee4d7e2249c06488beb8529983ccfa4b0f8d14a793cfc4dae4160397ec33fee08432d77f3d0e3f79717c17844f97047f5ab7cc2759d39753c818de64bc021464ae47a251ddf25430edd1cd9d2c33f3b56f814a8c34b13203743762b47cab28913096042cc25aac55ccd2d72ad3112163a026551e35c9130b5668d8be0f4b7b9aeff97706244746374c3bfa2e1add88ebfc1b8796a16b3b307e394d5d4e759bf1a80d30358c6095979bbb784ff6fafede4a02b03e373456bb16df0dc10406d86362c2525ef6c34fb0901a37c70a3b861b899ab495119d0269ecb04022824cf4f810a750b632032f9de5b02b35fd8c7a4372e1acf0e874f12a7d06c998415c1f15c39dd774e338df5185caff419977af6633188e26a92a01beb729ac90e8f010bed0a1fb65f45eb3d4ba264eca084501d9f41e401a63e51ccbaff324851b8d0a92c9285d7aff3e9775edee00325210e5214b8d4c02f4af13e50021210d6ace834200b4715405b2cf28bc95f495cfcdbb9566077134ea896fbba6a8c3000a59d7b830615b4a2ff9ef8f19bf59dd230c52184b52f466207111af8b2f87913fed05d11c5e19b28de3dd1aab06e13bd529122d0f14d68d5ce86dbada314b107b9bad8c2493c99ce3cd57e053094613f0e02b9ae4772612417e5e3fe08ebd32d089c019ddde7db78ae339014604e302fe742706eea5d83c92ce24b713893e7d91151a49d84f82e0066eb446ca003f5deec166eff1de11fc5cb8c25b2aa03e39b119444aaccfcd6be4191cf5bcafeb7075e863ac28f07c1e61aa4a7e3a0ba9787e9b7c1320507dae6581082e0357838953a9d9cba224b9eff5872a409ade803322e1943ce745f684afd858f5157833afdce8ce7038111ed46dd2a5c1d53f8c26e4010cd709d1ad18eba2c34bcdc6d065a77c1f2469f5977fbd3d3ae36aadfc51723be6cfa9a8f20e1231e43e6c81b843f61af73d6852d2f4d02bc4e9f0879db65470daba140cea661e432ef73fc21f5f4b071b6d2b1d36a03299a45e734a04c440c711be7c7dd6f7df4390a34026e9e2941641505ddb8544259b626dc90938759e969400e43298236ece48a955fb9cbfcbf3bade3473d154706cb331449576909252859c1071d965602823093d0ef83cbabb579c231d33a34bce7e92d94ef263889cd6cb69f9521c216b236d5184902533bfb39fcfdc2c64ec420ae07314be27bd6caa088aba191ba76dc96d8772e59b9cd8159b0b003dd152240182ce29b1c4cfaca71b3fc0391adf8a5185a92afb54c90699c413e5cc92a10b4ce11d3e6ea956fa1785dd7d5bd4d7728d0367787cecd6d0a9ecf1c79397652ba19e2baae4b131e98a037707e2faac581050231920964c6db98ca2279a5b14bd962ccce2d90614ee5d75b99076abb1b3a417716f9a9ceab28d31adddc2508d825823cee80c943040b8c1ecae6e95a6758a702f0182803a8614feec4ec2546792af032aabb222de65dfd83726097c392762aade4e6b64db9922f81a402eae0c4f8b59e987e4d1d045e3dac6f4d3241d7f02d8be3b4824f5552db334c1bc9ae54d8ea11293fdbb3eee5b6f63f9e33fe204a9a3ddd31772b8363e1397376bcd485620cbf09b0350d677fdb90c979c06c580c2ba2f4ea1f129b6953e1c4c5f3a282c12b4384a44ff071d99f9d7fdecc43e0961e7fe99a42fe31b330242ab502697bcad832e0e0775b2e50e956a8bcffacc599072f65b0e1f5d289908058c27040dc5af3bed701828f4aecb1091d0d0a662278fc0901182251d53c308476f2928c691d46d96d96c04fdb25e58387f51a2f5328e3690d80f6bb8215f5c7e74d875aaeca112e98bfb315cd5411c4cbf5d268b87dffdd719d93395602952de4067e2bf1ad63b17da8b77fbc41d6db21b80a39f91454fbaad73a073cbf1a596bf9d006cb312951115733909b208b6ef5dae64d45cac2d14374e86d56025a595140c8b229366811f66394882cc15cc220536caf306e19a23c2aa16726581f1023cd4b42c498a140e07717bda289e6c0c0aa98fc930ec983cb11f795248054107a1c4fa5b9f2e00c4d8fb3e3483a970bb39480792c0159baef24862be36efcfaf0c8ab49a98c1457f216a906e309dd2d069af524345c46241b7910a57b5832ca1b0f12185bc8515a800426ada554c9725723a8ed5d4db2311cc6d91c96516d208aa373ddd342aa09287fbdacf6db91cee6a256d602e8efde60718889dd4bc1fa391c7d10b61a9b669ec11ae3ce04edaa340d1362f7408b11d80e96a2aa1e3997e46ca0f6b80c2303e5277f9127065791f41e1608181c196152e8654a5925f20dc29db2888d44542f3d9224d2928d08558bcb64e5709111bdc8a90eb21b40bac841a6ecf4bab81deb800308348dcb0f7759014a8cbfdc2496d48008fc424cd53bd5c8bb12f00426d465efcc27017ff01117df933c307902c9b1f78ac7133cbfc7c90ce9d1091d866a23ef1c293f920e49527c054337e2e208528e01e728ca7e961c687fd0125585d2e73d4d2e5d7df3e2cff1ac45cd181e9d3c610bd5827df2933d396e8bc2699fe62a47df4b7b8d12250b1ce7fe9e18f73d271049457ade970ed5cc104cc57265f9ba434adb9a643c83b64fb8f972302312dd8745e27ddf13882c440e4f371a5a58d40521b3185e6f485017f08c9fbfe604300b98f65b91228614abaecba29946dc6457feb041529d534f673b204cc23b928d35d029deca04ced28f2a559ca7b17e35d4692baefc34c6a2e7450b25235f2ffac9aa0d9a22fc2b182431432129ff1b3a7e23050b3348e918802ac38cca7eb31db7707fc7123178fdf80a4e4a2470d26552120ebc1ba9d52639d0c0e199fc09153a85aa96dcc3da16d548c2485653793e6f0125448c48dc35bc76900583dcc12296a0f58fa2dc4c0905223011dd11bf62d1929604438a50a53b50ea7b6878cfbb4904d34584fd378e23156008079cececcba84ed657cd5eea3cff2898a0e69e69b8702f51c34faa1d97b9b4aa0bd7c636c4ce588dd62c9058ae58e63123a8e15674851e544530feef100d728dd92482a50128e893e8de94aa0741cf2f6c72f160ee97fddf9eee90103881ef6d452c9fdd371ac0de3c94a80762f6a48366be712a18167b548cd9c36ef02e8da62b5199b57618831c777b6e146ad30b88f05511a35c5eafc6991bbe70dd8f823acf858c6495c359908e0ef0297c9eabda5240028a5580dac2e10c0b866dcfa99d9c2a01d1083a6233c89b3972f3e6716b3d1dc8e05108129d15f96acc17d80a3f2db88f02393e45fa1469ea992cc18af2ab43acb03a9ef935e02cc8619586b5a61f3fceb0d65301a088ab351b5086b69a39b38c9a5f6cdf5b197df13bdbda154a38289bf7511ac7aa7938b659c04064e4f62b3cf0a18b4962f355f2c30ae04ecab0e883103f929eb7ad12c5c6accf38ad87e379e8c6bb267182ce99cb325c981906915edaa431a30d3f04239837da4d478f147e168efcd7567a10c19b65d99b3c0791bedb13c06bf503427f6cad44c6acbf1dbcf0f3f7ad2f0bf73c29e3f554c2c6c0d08339458b24ef50a50a75bbec178c7b122099c66c76da55f7bd385e0a9dbfdd5db8888c00f5e495c85bb7224477a30b529be5ea22315c87bb08dc4ba9e5aa6022775fad43c614ca8c9f221b132c48f0c0cb09b88aaf2a37e823c83b0763fb195dd33f8a3e882abf2ce06ffb7e0127dfef582cce50f8360c385ca924bfbdae1d37286c66616bc52a68d04d9255c5c581ec04d3a79d33d63ca75f834c065a08eab5c8b1306bab19ee1880947350fde58a8c6b1679b97f52d2010996abcfcf5c603e627936f15663113962829c71de5e981e2ed24c27d638e841c20bf50b505ae1ce6a143c264c6ad1c1ac0472e3a0628d90434210fe4e411013d02863c5d7b6ee606b56de43c817073d47cb7bad6fe623b027b380b449762a63bbc0572120720a7088f3e6d49e6f1fdfd3a7f3f8acebb07acd3dd8a7638720d9cc9e9cb6a5d6cec078f21452c31db6dcc89b9cc50a0e0c2f04f7e06438f96d92cd814a3e26c51e2cb3360cd1de21f366052350b26179e7c9c3760e56d4c36bd0d08e0a8bf68ddd3b0d810540ad4e01d06a0fbf08f9094edfd555806d24eeeb2445a45712b74da867b7cac839d5734b41e535dce74dd65b340fa12051af36a80d6649e6760577b8460eeb42d9ab0d6c1664da18c2f6950f4eeeee272a459e42e03815c8997ba9c44891a2adc01ce0d4c5d8781582d0abb4068f53b22a3d7db28ecd97db37cb3f65590b1772af6cc9bbc72893b7d1ee3a94eb2930ecb113cf02ec0ee2b92d85f244d9b31a8831a6c2316c60bd9aefbf168b121c3caa94aa3d94f4417137a16afa7faa85c522d34903eca1ecdc503f7acca8283b0a6b8c8a44d06dca87ef840ebbc4123294aa6247e8ff64051f27849d02856c5c5493e5a01213159d4040092e12bc5dee94c0f123b84838b8d4cce605a071abfd5918395bf426f100683b53b59cfb2d4cb86e722864a27130084e72f21a621877ca148c8dbb07d3bc3cc7e2fdd1c76a9585aac21350d127a13b344a771bf19e4cfb08d7d0adc1d9829375bb6e6373eb26d1af92be8832c49a1c83a2977fd1b85114b0892a826342122834645f258b13454e737286c688dc18a710f18c12210ccf05e012888ec00f76a6d5ac3424e328105cfbdf5ab2afceaab1f4824efd148dcb58026f7bfad7b818234f960fcfba985abfb0e2db7deab62b6576c718aa41cb03cab2968dc91b2449022b16c16e1f36465bbce9b248613134dd1e67de1dc2d88360a5a3790c247e63e801d4a2a7f7a241bb332005e90eeef3d027d85864ee15eecfdcf0f7b107befd584711c4825f2dc4ada607f46b0551669dd850855ec224127138a12e131ab93703829833ca3d93d60b3e188aede330ab6dcd7a024378002b3f537eda5184b4bb25076b66d5aaf594c520e2e183cdc8ee377781ed410d73a783ba98ee462dd9a652d44913594f781d0bc68d49825067e1142ec09c814178829267a07befe77950753410433ac311035675c49018d4aa64b45bf777f6f62d7202ce792ec3e81acd63dc14759e67cb9adc59979e6c1dd1c818a5bbb1aa5c598288e069849b140b27d27182585d9ee6dd350c4500bb4cf890b58f9bfef37aaf3778f217fc5b644983398d3f7ffd43bbceebd3915dc9cfb8720a304207471c7b0030d117ea0302a2df6170460661cdccc72794bd72e8472db9cbd597dc5826703921dbe5dd4f689c207e2137538adc719841ce21f9e0321304bf6dea11619b416c3870a0985d2e5ac356ca333a57efa1d8582e5abfff2807caf5dd562e2107d15d1dfd263dd538ac5431a9340de46870ca01f3dcb2e9c043091724f93171856ee2f75c17b721b5761da1c3ee1aa672d9a7289fa7596a72b70724c320060e5b71beefa6cdc76a9ac10d31dcda1621c9f226511cff4df3d63f61353d74b858ee675e7b98fcb72611bb35fa1f1f1703dd536daff38643b2bc1428d9345db9466e5806cc4a6b4f4182aa29109926fcc9b2d1297927c02b342852415c487c6c9880d4180d2aed04c879275e4d4c713544ba40b3fa87b55573d929a12ab77b05cc17404d939fe540a8130bde1c6fb41f585bbd9cff51df46ee66250591140287240e8d38d90ff4fc3ef1fd95a68ba72a7060e89f8373b97793cfe9d53df9c7e3090fb45e5a5ea6115e40dee700646287ae20146a87fb41a3ee8b0e62bc3d6b331dbcc967ad345f1a07cefe5878379c221c11e21f67a6d650f8ba1a99299dd0e12f73a514e9d9918f9feb905cbb204faed689fe9d9f1c6dac1e91af931810e4641209b35daa460b03e5c4b63b9af3cff40263cb5b6050cfd9d4d82e9e5cd103d19fbac85de75eed398223a73a2cdba173ca1997a397c43b0a0ac4e605fc60565d2fc78a8954208d9a22582bec279c3af916828e2ddd6441fd1707c387d6026a1d3d188ef2643c3a29e68ca04c0dcecb1189348e2679c7d9b9098868b076b2290e6ff451c575480b8c16ad2b9cf2727fa8969f7c497fd249aeaa81a4851ff87b0f4fe82633823ed69aa0f4aea506d869d0897ced1a38a222999508bcd8f15505e06340619c72f0d898070b2d948a284768642951a5fa1d733982b8f7cc102ffc39c20398b7cd2880dc53e90c3b1096aba5bacce28b763408f2b18304794cda8d4a703449ffb3fafd48a42faf9f69ea7ebec9403f083332fec20184374e95d766b810e6edc229f5da965123838d0b93b9a82ed4e2659ba7b4be71fa919ffee6f61b298f7da2058bedca854410cc869289896d6e018c6d5ae41a4e850a9ea016ebf589213e3daf07876b89cf5dd370d23b90ca5d3ec717760d39641297f339781f25213d375aa432bf9acf7177d499730d93b993adc97c8f4fc462780dc0c65199c7a18b47536398ce14b47d1103ea28f8a897ff1b736009050222a2d5d4bcf7192e35ae7947000085bd33637ec2503061e09b3634437de1d822c63db7af7323bde46df43b808ff79874a5cadb80e82ffcd23ff1c76ab2aea15149f097d7912f84f485d10618a4afae7a1ba13d33761f40358434a97d04a301805d9092c51f7c2c703c2b4050f564058018e5b489803c8e0bd6d75b46b3bbe287aca3b3606cd9dae24511c90af8031d30eb3a70a20bf70573d37c5519a28a419524c7846a6d0a7ed2b4029a843ff357c776f7a5a8157041ad7015a21d68a1a1e11bc42aa37523a7301143f8cb2f507b8116cb11e3a85298cdcee4a8bd803db3a3c3d6d9dc9f070cfb3346cbefc7546445c3656a8877971dcb72e534dd94292d85af69d4312b9a318909f6a930cf57935d780b9a5037db0299edd1247d7cbc1580872a424897e482d04636b0893122f45422994e4015a2f994303e82b0980b50f6c923892f665483b9af4f905e95014dc9319dada397cf473b166a030e128de7766de7c1f40d93ac347f9456514c030c02d002de987ccb042b2d509e03a3faa5a2f129251a9f355bd00aece6b456053887ec0376d732e97b40cb361b4d94838a0738090fd20b85990aff1f5d4df4900114c0e1e43e40055f0ff40d4049f1dd15be3b0be0cc026c6d2e2e70c5b08b56437a606f91dd1dadc5fb9cd822bc754415e8bef8a9011da65020cdaa2f0b8244795a50c0c5550f31a9b1225cf78b2759746de6e4effb5719d843af419fdda4dfd10d43449b3a80336ce552b812021de4cfba6b8690e693b889f82a7cd3388c83082acd51d44a3cf70f69152bcd8b0aeb8817cc10c698d2ca74a2b599e144058a4d5475a709c67744addd1d29ea412222678d577fd4b859ed6201bcfb9d10570e346fc175ee094f038b85fc0ac29fd4ef4cf1d8b5ecef8abdf29cecf08b371cf91d581bc40c0d32c7c7d76b231693763942620f12a91222e08fe6870c1e2c79bc3067c74e7b35a0fdf96604f0fb5152c608cae26371a6f2ff066a733dd626ae1b420026222c3a22cfc49754d413d96d1a51b1469aa2e23081a5e9bd116d94803e5b03eaaa0ec843840f5fda33592723eadfb1fc89cba827f63ec834d6f940ab7c458e0f3ddf71e529427b2aa4060e3806302c60aa638cab9b078a0e5f000e3c5ff9cd7f1894b56528b8f3d9c59935ba22a9bfa2011346da1c313dd56c846d62ba11ed35dd376dd9f4dea44428627c650e4bc7acd49ce4960d719954592c215c5f91947c85ce9a0f0dca196c9adc8d9111ccc1de1845a7340a0d4a9d341374a5c170d2e6c221c349a9d66ac54cd923812566431f9e8152c2edd0d63bb6c7336222010c7e40737fc34b49b945f7cd9d1493c273ae96b07e2d164f94fa4e458423afe2ff3f82bc1a21d6310d4dc51f7ea91d412bd941ca42d493c8036735da9841286937910f9cc3889115d1205cb868a03e030e2fa6ae78e2a1a11cbe0a4706b0f6c381c08889698e383effd424881110c58044aaf2e9061e9ca87f626f480fec4f04ea98d3b7572092b29a0e14f1df2691be54a7273f9211fff7b90beabd3a8013d7358a73bd18bda74ee743039f8124b5d668ca5a478d0a29a2450e2b0100ed6e3254b59e5757da31c28adc5ef9c198cdf3877423bbdb967b4b99924c01640bb50b620b344843322ca129aad048a1e50c2d4f5a9ca4a032b31eb090b024f54009e98824248efa91130f335b7ca05486d38c8da718698c92a90a21a54be9b2c10f4e4e5801033ab22c608e18a45839024b185dc8a257014687279cd8418a0f186278e1c11523a878830859740984cbdb2e6aa592d690f59206447031d425891d6aa0814cca99afc272e8d6a68b710ff8f630e6178bef1809bb4bcb64845f6d6485b4f71a953d4a8ea272610c49f0d51281d58d1d605fed3e7cc8c87ca8427d88fa50f5cd51d49520093f547f23ed48da59684fd2eee4840f8c9ebdbed857f610ddb489a0571afa0dcf9d04304a31d55fe5963480117af69a58e34835f4188e1a7a9e17866ed10bbb8a6c4a91292eb122d3e67016a73947b7b997da462c35fdd07c56d6aca63ed1533d8986c54f308cf998673082f27bfdf7c98fa58f606c92a13e8122d7983dbb07238ade43b8960833ad1076f6a589c397a5249eb58ef87ed2292eb9968084868a8c8e94965c4b2ea727a6265eb261555ea286bcb4c437a17b719bf9339978357ec2fa3ddb23c854d8c55fce360e35649f9f2fe6f9a7890fdfb4af83ad3e825906f18c6761aef82c49a7c22f76ad4aed226a7c6217659f98d44e54c95ca48bd230690c3d7b903d9a2509e3a23482e9a48f805fec928bca1e6fa8ec60bc6d72e3d9a5530c83057b800dab89cab3d77839a8a1a45ec8e9f1cb57f6e770bb93804617e728996ae81bd96da4dc4117656b1332126800c9a5a10f25d003c92a9e18faf2ecbcc4e66b13caa3670fa2c518679c1f0c1c32638c73e3fafc6c9c97605711975a430924813ac651f6c54e83500e43ef43e4a19f29518fb41b74dacff3955e825fecfca5863d54c437cc00168a240cf5127c133df6874bbc6f90836ca87ce52395389b016a9f09e29bb087866658886351b65b7a30d43eecea9eee79f60e88af7cecf9b087f88a10c726161ef2893db18735f34c46c7195904248bd8159b427388e513b20805a1a267a10e127af60e6a21af6a3f1b0fba1ea2d8be2b71c2f621ecc27150493bcc0baa8bd3d2bfe8f411749951e50bdc3d9ee1861935f4677679334c460d61b0212b8fb4d4e1ed195a9e5d6a3ce79c923e81c20a6097375476ce52d967667413df84f1e9698ed16e3481994c0d865fecd38a1ac627c6526751c7ea50189bb69bc26e32ea571ff58bbd09c6e48a4ffeacf8149f96bab0d20a31ea174ed3b3e394307ff03e7a6110d6962f41cf5ea7b88c28a4f2ab5b672238c32e76b1b3f71c9d1a26840f952d66143d7bec89f18b3d3eac00f659430dbd38633d370a333c8b53aaed619cb1e26c3f865cc7b34379fa1ea2a63bec9ab102d839196af5d8b519d57086e322d650c37dea5913ab9e4ad4decdbbbbf2e322ea13283b7c135deec42fb332ae3c3b9418e7dce283a3393b8c33283bec62f049fcf2ecf14b749a55ba286322ea0cd094678f416478e966ece24e8ad14f1f325018c17c283fec9f1e8e5516952bece21f312a0ffae7abac5243a642857ff66bb9cca2931e473dfa31b8f06bb7a85d585efc8b0b0cecc770835f4b86d08774b77876a6a5e85ff8a6490cf3cbc5e8e810d4f14ce9f775acf4f0cdd26006a3df3e217e0a85f8ada30313dfb4dae337f85b80f080e843984e9dbf5b7800c3b2ed79edadd3f700a1055efab7af65ba7d3fbd5d82e0eb140ab20735965167181d47bdeb8ad6d65a6b4ddbb63935dfb44939ca1eea8a76cca890d2198f5615f5666043c018a276dcc54faa0714cac9d3b2e2943a15e5420855476ae362c62d5135260ac46a2a156d563aa934d37c50dd346167d0b047695592610e9e393e0f813d0c67a861fd85169e4771f0c303201e08d978f528a595e53c8250995121e73b10c393cf36cf5e43cf9bb94113a51da8da1713a8315c5aa83f7eb7ec208917c10e80de9bd9810fbff6b71dfcf06b3fa4286fa6e6fbddd265c9660641c66677c15851722ec1b0bbdbb5d69aaed6524ac975ccc6eea667042604932e5b4aef4f881aa5f4d8f5491416b2bb89ba5e440dbd5fa75348a5ed3b52925258cc3e1d8b43f109e53164a18fd1a79498f49b149362524ca2cc85b70039895d4d5c9e62130519dc641ceabceb2f826962576c50f66cad29189d36308269aa124abf62ab49ec9c9d5860509fc70aa028d0773a5f59e4c01d0e4ac7903c3d7d442eb4e035293d3b027e5312d09ec812e6eb4c8b7d6528efbe16834d649cb30fb573300bab5fd1b75face8483a86b39b466d5a223e9e888c79eca3c7a6e8149f66ace445e9765e610ec3090284e1c5062fddf5b25372f65043fa4b3738e0a52f69f1d23de9f586973e239da5832e5e7a8d0ecc78e936d2c56dbc741338c6b3d96c66c34f7f137976fac333e039075f3c0e184ccfac2f83ad7cf7b58655036e60c8ded2cd511c846fba2d600cf1548636cfbb4e5d351cb3e15795f95e6079f54e7ea16515e32be700ab6e3da0a807060d2a5f59b7a994f3f858de7d216c27ca643c08dff086f2986f27c67dd8a0c020ec628ffe78cc477484c6592e533ab4a5428cf3f02331cec307b8e3c3639c824778380bdca1711ede29ea9d16a36365097008bb586908bba2525492718619e9f1016ebf68c0ed57b7b2986f653cbe956df593f1d5b7437424c6659c87779bd77dbbe58b3a9e7ea9df140af15b2aacbc3a51e73cc09d1ebef20e3c525d06dc89f19ad2e9f8863bcf6b7e5043160b7476b1cb944e471b6471aa49fc13ac1076af9b849fa5f6669c3d9e608974e013eb2a0fa312938ec5226eae2d674ae3218be5a1fbf0d0f5c343d8f7e3db7df9f83a677d9dd37c3c3e99af03c39c8ee95ce5dcd319a0c80b13c73e197b6c628f60d8a31bb10ebed92d601c3d3bcff8c667aa3a261d0bc139e643d22ff61edff60bd56058db2b82b18bfd8e4a57b0faad0c15c2d743147a3df899af874886a75f68c3ea206a1895664f7d0e15191d2529fd5c7aea32496989a9c9e909e8e58ff491b327a7a6a7de4c1b5b9951d19010d0cfb7cf53ef190b6d6c6543454647494abcf4e4d4c4b4a49404f4e313674f4edb148f58d8d8ca38a147790e77b4b2dc050b71729a7e9c775d93edc3f82d2cd3a336ce6317bee194bca89ca5330cf6a855e87aef5707862857398f4fe6db8ef9ea97924e3f2d453fa6549f4a7d5956200c301991f428650a3b1c1c18cce57267b1288ad2133adfbcaf9d7ed3bb907f366edb569ec39f65d54b5409a6864b602feb0d358c4a5f00f1bb1e97d4a8e192e993677c50bf5ebb28c593e2cf8e19a7f8c304ca12e98ef948e9530af54d7323abb446d825918047d825a5c72df846ba942e619c067bf9dd07e563f2845db2939d6b9ec34c6e3a50765bd781210ae4c027ec92145c0d0adfd4972ffd09dd18bb4b77d7ca951b953431bab509e31153e55f57fd1af9cde717c276a28cbdd3901a8aa338df79299594ae9272ba8ccbda32206b5fd22595a0c4a1288ea2507484d917331447635c4aa95491b0d03176e91d448d1ee230192f3de6ebf9ed5b027bcd67c74c6f512a7c13e24c4a632228b13082241cfb64d29974017ae94f8e64d27796fe7bb9a5cbcf4b8f3ef187e93671582f6378096a9ae673e2e494304745d2dbbd64997e4ac7e9584db554606f27d27ca79dc1d6f4951d59790577d857606bca68bfa474b9496204714312685c59030b994cc18b0d4932b011870e3ac8a4cf125266d8610839e80843269dc9c6645bf824095db1f286914cfada48c9d371375b704aa94c3a0663c5278a6ad4128dd815a3cb396a187f62f46864957e76957e7695a69cf28b3fec42c94e7a528221074620305ea9d1eb7418a87ee494ab54be728e8221abfa06865593015df105eebe24b8faeabed655a9665f90e391c7930e4dd3341484215a63c80a258270854c9b0118375011638a1c51649a94d7b680e3b5edb52dec78ad874cd3342d46796d0b315ed3b620e3b510649ac6d3c41569f8f0823482aa38e2c1610727a87823e98b2129bc70c288057831e48590fcdd72c5173cbc55d43829dba68ba467aff125d2a485531747a1cd1c918b375e4a3248ea68975faf72a55796f36595976e0292152418c7602ebc7c57cc6ca004d734d3810cca48420d2678d0618618e669037fcce2db5bab810e38500185174818a1e00754aae822480d249ad80108556adc162825cbb7d76c3a6801e41c7d2049d9e165a250f1ed3f6a103b441a2fd6f876164f12453c3147962b6198a1034f146fc0b1c4831733e8f872c50a1656a22c9110e21748331ebebdc7d450a86fdf7656bbbb3af601293f3ff0c086208062206b87754cce30458e3994a0a38a1cb2d6e1bbe7d4664be93c523a3292f1ec92eb48e2d90819731d3878b6a388e7c20e379e3d481432f6213877103d7b09ec396162787613380c183fa364e54a0d3df1a64a3a60f0ed31ab74b3f5f6ddcd822eee48c2624418a72bb24622c20c2c946270c1153090b5956f187c77912ed2311637b102dfc49643b2958389672c9e9d25c71bcf5ef30412cf5a3ccfe1c63fe0598e29cfbe4ae930ceaa51451337f8820839a2680303327822630b9529ea4022071f8dd53555d6530d4af8f6544aa763194f4cd43cbb97d2611d8c98d2136f5012290e30dfdee1100710cf2ed9b9940ef300471c0278769ad2611d66f0a3ad121c59be7d83801356b4c8524515550821631b10e520280636ccc0051719ff58d172d37d917e4597520651599105870c0f7efbdc7450e227f6cbcbb74bbef2f3ec26b04f00c90a2ccc31a69406f87e8389a737d4d063d5b8e1c6b3c71913006e68a325329ddcf8f2bbc58da10fa9101f3dc8ef16378c9e0a7deb257802aba96e033d9de1a68d4636d6401bc4db18e31cc1763ab12bc6e858d9e2c3d9349bd8359d2618769940886f0f67934c19dffe256c2a328ddada7c3beaf940fba8b5b4110fd55bedfb9cc6bea203c169caf1314699719353cf5608ffd41976c506c337e16c8a4c3f47cd257a2771ccebc2b13d8ade5ea23713dfac3e7a7fe19bddd2c6d147efa676e298278b2e9b67535ce263f4e954349fe21351f848f1d1b99f190069118a6d0cb9f1512b52e2a3731e5d335ae2a3531c3eba7684c647e7c02a1f9dd160b958aead68336aed878b8cb8888b8ab8888b8c8e929658099ce84570929698589bb9c13721fb3cf1ece9e9e9e9a9632b9bf9c427166a63bf7c5938df14077826e34024fd8a1e515d19e74daffd225b19f57033c0f91ae1c0963693693e7136c92159248de451c7e2648ada4cf3d17e36a7e8db53a43f1fa9cf473afb389ba6d377d4b1e93459d559b3c935a024a525a6d8349bd815dba88f3aa995660065478a25c2de021ff5d16c36eb9945b144da5b706a451f2adf7e799f277eaefcf8c812c694246cb0831843cb0697227c90411d54dad841165b112808287a5c220ab10bcb6cf239a736a34202b801d36e4c03f9099ee0f22b642dcf2017f6ca5a22512af52b898b8a8c8eda3b72d10a6132aa04a9e45d77b5d877a324d18319cc90218e2853c80c85a181c9075594a490ad771136874d388b9ea373cff826822dca2eeed912e9d1346cccc8bed889c44e621dc551e4483c370d517dc8e15903683c976063beaff5389ecd78ee563fc137214e77b483a9a954f1ecfdc3312fb64aec8da5e879f5bb25084653f604f88671b6b8e1d973865079f69e35d00fe19b5532b244da87592b64637c854c2f6689c48f9aaf9008ee14e957470edc31d2afe85d7f21cf9ec33814aed2af52cf8e929496989a9e78957ad6402d34347de60ff7149a403d5bdaaf489d0329b87dad2320056bbfda7bbb487297838db1f28415b6aff58555a4a0177c04c3aa7d08fbf5220b5bc759cf592fd2566ce4286a8ba313cd51d8ef3eb213e99bb7f7d5786b5f0afc382774cf72f2847e084b42fb13d64fb09e5137a9c804f8c6eb3a640eed45da8bf4c228ad95d9877c38ecea2e6a749ea2b61709f2ed0b2e0c27a7088fdf2d40707ad6e20161e9a3c3be617c0383e1e414398a59398b5d7485b00f16e6ac36a774a2c7a50a65876f62bc63fbe2e1ed5f7b10be5179cadb87f08de7ed387cd3396ae57d42ff37f5f675ea06613cc002f4cb442a2c0dfcdea532e4f0428b38e028aa41d64a414a3cc111c30b988042d61ef37943685167d66f5708fbea53a5befacdb0b8ed01ec82d80e6b16cb5dedb01f7aa55b1b56f9de8038fd6a5f810d762d6e31c82c82ab4095ef8ce02b70e53c32d5573f1ac6683ed5301a292df908467a001121c94753c75a4f1d0be1f356293175ec87b747a7687414b544232a4d49d848c7803880c69dc81d887fabcf5df501f12fa502cb69be9575dd9444a6b71389b145faae35f8de5a9bd6db766374c42549a98b17a62f4e9bc6434d3a3a7c8957aec42b57b6f839e10fe34c827136a3a1dee3239abea926146c519c25c279fb023616a3582e1a711e46a368148d8eb8242975f1c2f4a5c9c9434523ee41718ed3b168148db868b4711e53db46b3f9062e7b387cb3a4048e31e957b31455be13f9e136e9386951d0880371e9345f4f4fcb7d78cf0f677907e4fb7c84af878bf0d1b87f3b444768fc73d03b8d9ad4b11e47db12d9b65e3900fcc8ca0100ee106d0e823b3d7ce51b7844e51fb843e3dfca817c2a1fe15b9908dfca6abe95b562aa65a407cd7eca6bbe196f7d3cfcc727e33ebe986d03650f0f8ff11e19af2e3735651276753cea577bfda252bf9aa95fedd4af8e49df4fdfd1885dcd6409bb9a988607c53cb0746bc3a954295a5d3011e88f1f1e55de3ebc3d0e758ce53d53e39df25e79d37887e05076d8d532895ded5064c75cd4d5a9be96a7be1f5ef345daf21fbe291fbeea44f8883a9b1ee0f708df5a0ff10dc76c988ab0a3b5864c7b2c6a99a49d49c77a88da48c74ae812bebb8415d29bd2096185ccac90de5c848fa8f34d35d9c0d6f6c3a51be9b18215d2aa8f05f1d59e8a529e03515038b67714cdee88d1c8da6350c7f41ec52246711d14767161573b8a66f9e1fcfdf0d4e7536529577d3d5360329f2afb0142e957435921edb28e4ac16d8d7a4c694ee7055656a3f9061a69b57e380a786abe9e1e96fbf01e1a5f79e75a22aad6a7f21f5fca598ada22788d231101dc7ed5802dcd6b7c65485a1ad85241e5359e0195d7802f101df9e1297fa1056620e52d6f812ffc00b75f9a8f6f6553c6637ce5e3abcefa649ce68bf199cff3103e1e5e7d155f7e2d232a55ca470fb0a74706cf79784f4f8cf7ac3c15c2b7b2994fb6af84fa55d4af76557b6d97f18ca02039c2ae6e2525191e8e2b12281a9a558f1eab964743a3e2115323c3aa3e787c3d5ce6a3f19a2fca562ef3a9dcebe1345ef3ad6c5b34ab896a71342b54e7751daa43799ea33a94d7a1be2c3d7af86ae534ab1eed9b6a71a0911e349ec35d4cfceadd8c1fc297721fdf8cfff87878fd649cf5d5f9c3c77f2bab3c1cab8ce8d6a6a7f39e2954167d4a95c5f4f8c064313ea5caaaafb346f0114470208ef2adf15d4ad2b1954b87d2b11e3f5c86e0d29174cc5534eec325cba564015c815d7269894e71731c6297f4ee56df08aefa6adcbf28eba2d7e38b719aaf3a6a04af719fde01f988f863a17ac8bcf46e4ea9ec71cc86a9083b5a45ccd628620d19229c58267d361721e2cb1b81d9f0d26b68cdcb1a6e85d015624765e714e7403e220e6c7135fe42cbabf7f85e203ad2f2ea2f88e0311b582132e6e340ec99e2b28d0908555101e196f11dd879280f3c81dfeb1c09c7f60e1beea0b1c1e70c1b7c1063c3eca5479f9549f73a541314d6fbbe88855dd23dceb272fe92b04b7afcb2f850d9caebe75365d563be9e293059f5a9b21588a45f12491d95a754f698a29f13ead3bb1f5386cace2d0e3432c208358e029effe13e7c7eade919e8e1319e011aafded57c2dea409c4736c2d7a2604b8595d7f89195d738f51a7087888247540e04dc69b9ca47701e19ac5594450b49688b35a090411ae20b47618b2454f4237c3b4423f8a64e00e247560e04dc21e27c04704704b045bd87c7f8ca39f088ca6bc09d16d8a2606b822af4f0183f42e3d569bcfbf1f1701f9f8c87f0a59cf5cd5c60653d3c5cc63fc6abb78cacfc022b5b7906ba0d99eac7b7321ffbac6f65467a7a6448f98cf7f4f0f09e185f798f8cf75457d17cdbaf0e614a67a15fd2ebc7a45fd265be38eb97f4992ffef44bfaea93cee3939e8a403f23f3ad6cf5ad4cf5ad2cf5f1cc90e08043d8255d8e191e8e635c307b6a4719d75e613641611f09bbda67075497246157cb51b7746b732493ba7463fc98c9c1924a51e46000464c5c9143ecf173146c61bf78dcddd9f36c4670eeeea66973c266154260f9450202ccc77e9180a8e3e57a32a55d511709882e1fca8e44701983ca4b9f913d9b67b41261a6c720d44dc9fa00b6ed93a01cf6e1a6e13c52caae8361275214c8813d5384c838f7e121a33fbc242aecc399df6fe679fc74cee93c82229ad32798257ab860080392354316c3a5e3fb8a74ce000939d81226dcf96dd261625efa5a01f34fe26b4d3a4c989f60961760d81bfcc82238730986db33cfbe290eec0d7e640cee4467f5ab67fe8d20288480a288c96752892a454821620a59cf142132761f1eb2e83d535846ddc7013276eef371808cee101d61df41d23b444738a7e04e117087e808756ea7bd70cc9045161f849104195dc856d63c3b6c314510416c5972a20dd9caa28738473c87b539a59ccca5f1a5dbc1ceb973fa82ad699ae7b0a6edcaedeeee10b664075eaa49fc056996ea5403598c3a3dce5ebaa7c3627cd85a774f4dd3346d6e2ed9a5699ba6cd293730eca07474524aaec14423727185cb4a33e3056afce043b92b89eceecedd1fb2a7b27b0cf2a136a7e6da0a59dea1bbf204dfa03ad4e7a0c4f4441ee59decba0e0cc39862183e15a20086ef3c3abb949e7b6098f2762d7aeae34f49045d88d343a1109fa25c5a80c8a7ba30ce7eba06475da41c9c5e7ae75dc75e4c31f5a6de9d40b7a553e790accc5e3ae5542af5a54004e5213d81ba4ee7390a0c39e529308c8feabc03c3ce9fd05f21284751efcd7789285942e43bb00522ef04e54fe8ef0a694779b777285f219c6f4a02d2a987d1699c7774d1d746bba2b2c7219ed90a242dcf1b90d24a0de5d3962d93f03ccfb75270d574cb31f24eada7f61b35acdcd3e34f300c9fae90e85bbe3fa41d84d8c55dd6e5cccc5186d3c3deb6cd45299d73b2344dabfdea6eda2fc9391739dfd491389bc98eec918c8b577c7a3e53f966c150bad7ed576bd855594c33dfdc449d99deb395829c2f2520651ddde6beb974f9e9483a42a9f52e91dd5669ee393c37b64a9bcf2e888d722a5140390a44a150a8e5627c448fb27bc694148bd6d96948e8d71986a5465f66a35b1b19ac632da96f655775b9e3c0d855244b0e5145d246a28c896aec8147e1e20f3574d1013ecca1fd308bd48409b00368c4f8aa45109d87326685ac2ce2222a836e6da203ba06783101ede147590132df1eaa5e155d94c5375fec0e05b68aeca39ce7b94ffb42efb5f885503c23aa6f1ccfc7ef496b5fb85d5e7a4e4fd6010f6fa579aceacd44f666b6283154796666e68ddacc546fbd991ad62fd20f0c3e647de8cd1f9cd083131453700102194ef01c2187156d5861031454542124e50d3a983749ca19538a1a7187359c9c60af494b2e58964d0c2a8414247945174647b9a0ac9c4d4a164e4a11ad319a52a69894f5d3c373d720fd04750700931403212464e0c2081e29416022061b8cc088e306190bc172d78f0d3f33302cbcb61bddd07fd2a97d4fe66f921104559e2e670ce54fe9da94524aa56e6bca2f8eaccd295beb3967c775cdd85a6bdaacfc65ab8dcdd79aeda853df9b90e7a3731cc7291dce867af23985e89c8dcd6fee7b139d52ea9dd2a1361b5b80f3185dfb8cb04b73fae93859815d9a236197a6790ec767a0ba044cc22e6dbde89cc49604d637e7efe79f516a8803856f42261df37cfa0edf509f9c4f944f4a6b65b11c85f5cd09ca398faf4d8f3376f5449f48e563d2afe9def4e973ce39e79c737aa86fffbd8f67367d7bce3971d835635004c2f9397d6d26a8240ae1a09bf14d07396a3acae917bbd723af591b2fc908cca8d1738ec21cf776eabf79077e2b04fcc0c635e74f3400bce6df9260b38294dfba340f40075ea6302d209ff3ae37ca96aa26fd1ce85fd5a4f73a6d372cf9662a96ccfc9c93a9c908775a65d78c8c1b6c5fd1bdd0bf73ee5c7ad77d33deb386e26cbc96b27dc9cc4f90763718845d51898328120e3078d42f52953bb098e1f53ef49606f08b84830edefb45aa12042f66bc6e9c512bb0a77459beb4bab809c847d6be622c875fd197db2300b06c6a587e64c362d5e4408726258a465939ccb462c3cb61499901c586579305e5d451608e0a82a630e38d1bd690e20d197f3182888a163498e1c419b220ba6861684b709eba0928e4c4589a941ca2b0e15170d92983351b3804e6352c1b160e4868dd4838342159ee6afd22e150e5b55f241c7c3e842969d1c929757db7a0750ea94a9777fd225559e2bbd59eea2255a1f2ed5c975aa9a429d47e46d9a286975f97738974bea0f673bee7827ccfbb2fc98d6fb27acf3b76faae7ff09d04f3dd54e33bcdfbf2a8140ae077deed945d775dc75d17bbaed3b61faa9007cb778e54a5078ffa785ee521e7ac52323ddc3cb51e72aea95228701e5311a080055460b8ce39a7d3815cb2e8790fdc15d2cd39a7ef8fcec3e972fa824a386f57327d3b999a004f15bf1b44a628c0797360b8ecdcf6a180e7c10f77ee6e17b537baed98f49531166cf810c5102c343cc9fa5b59c7414eb0218301c5c64ebc830b26190c25001dc1854c7a7f3b718a0c86bd422641a256749e8f31980124838184398850da89329c3193c100eb59a30b7ec11c72547992f5276d1043d66d9efc769606464a32183a0699c70a88deeda6452efafa4088307c64fd753690f1179f8790a8ec5091a828215161e299b6435d731e19ca7954ce23abce2393711ed98cf3c842709e09b68ecccda7f3c838e791752b1e190f0fe1dba31a7de6ab3c3e960caacce7319f2b7afd60ab0fa78c1ac2728a7ca9e11e19f990f480fa5848e254c36562024506355ca78f31882906ed2b7a0c8a4131067db41249881e83a252943288637e842536138dca8849471f3d6e11bb708c23d39568c5051f3d5ed1a2952825626197500c9212f4d1a3941a66bf008021864732ada2934a5d971f63ec96991c3439accc50a452f872bf5ba220f986be2104b9532bddd2baceca29d2316d739c292573cfa08bbc52caededed955c6f37ebb2676597a7ed4a29a5dcf59cdeb9799b5a304cf2ebfd8538a871bf25299df56db9350dbbbdbb51deee6ecbeeee0e79bebeb12e512f3d968f9890e33e6a1f085e7a7f51a39ad7ce37bbae9e5be99472a31a1a949b63702889ea3c8f5381f4bcdd9cb7eece43711dddbc0ecbb6ddf9b20bd55e83924a7f916ad8e2819afa45aae1899a8afa45aa21091458630d42743d0cd113d4d52f520d3f23a8f517a986190d4ffddaeeab6151b94b2b0d5d3e64a1589bc2aa95c6c8a3a3491e651e8ca088e1c127a8b543d38f6745088986282b2b4f3fe09103130cd65c88c01ea57576c5e7ca4f6539143faed4b1434f65249fa195f7d0437ba0c2affd9056998f10eab3f1ea51249f590572001b5900a0faef163690c8014aa1c6fc6e61630609d4c181ea3951a076679c50696e50811f5c11a4aa7eb7fcc0e889fcc009d90c4f333431f3bbc58907a41994982fe8c2e8b55f1bc3f53072cfbfdeaf6defb4df1407bcf8f977edf1279f59fd72fa27fc7285acfcd95fffa638b047b266661a77eb1cfc5b21eb524ab969130ca3b6bb0bb2fbb7ecb0b9bb25333333c75f8622f217b2640fdadc505474fccb11e57108d4eb4cb1c2c59253971d8e90b1f350c71608b3a4198060c71251e440167f7876e6c2497ce3f97cc621e3d94b0043e90ba61adc60c411b211a4401ac28e3053b29821faf0ec485660a1d9932c61b2e5d99f40e938eb9a971168c33294c3337d5e9ad2c3143278ba32832519f36411e2810c3070d0069734641c812a752e4ac11e9d60bf4a3cbbb483861a30352185b14610647c864f105454a186147364e981bf73763edaf29e2721f33caf6bbce7214d89e2bd2dde439a4186f7dc866f6adea31f69951e2b3d7d978b0104f8ceb9944ec77cd4b12a830f8ffa1ee5a8940e2a8e7ae4c4affd9c304f4ab3251fa61f7eed8c9d7a70f09c734e533a5c8c841c20cd64dd1b4e4732ea5a4a87d60dcc6fbea574367a23c3f49acb948e26670c50fcf499d2997bc347b3996b369b45719acd64527a7c7414c7906fda458d7ca4f4d28baa1fea2abd7c9c4899a268a17992835451944595ac8e19e957f4caaace72d73e17238d45da1b1e0afb9aefae03368f0ef071d936e93697fbbc24158c2c917ef64d838cd7c1b393f5197f2210bb8238badfb2987993ab3171b4524b347a62f3a845283e118d6271280a4519555a39779aaf40242a90a626bbdc51a1ebe31c4964625567b91c49c756c6d4f484ea58ec43ac5ed4b7ac80e8dc87f36d531e8db529ed3b29ceeeeeeeaedcdd5db93335eceeaedc6eed8b13721764cd14e919967be17d437f3afdf8392be5ae942be5cacac58564dddd85f16ccae508428e93ced9d4894947aba435c992b04aba4e4277b010d5ab880c50fd6e71dbf8a828e8cac6517a5b967b2c11a983efc8c5b7c72c7ec1a8c5888a5abfdddb1b8d0576b5ef4df4dd9bcd4790846709df68ce557e9dbf709d581d8b4ea26b9b11c7e2143f2ec8c1fb41a80c29dfab2f2ee8d2e4c46d1b97a760e82348b2845d1d3f16509d72c3414e39379f9a4fea734a9f934e4e931bad95c5f2a7200dc55160f5139495559d156f50524576151887d8c5fe452c54d4306249aa3731372b8f72876e544a37139459f18dca39c6d98dc459c77878aae3b6982fd66ff5451eeca3028df48b3d058695e52e663dafeb43d6fc4d1591528eca728c98a3965ff4ee8e31ce6ec386b96839fce270cee99e53846f66d75967ad6058c4843d929e6342911967e714993e25e85d5143184ccb9194db2f529096af69706a4c1c29715c89749c1b09525aebcc8ce63925b08b719e59f8d8b587c3b13a9b49ef8a1ae2e08443c219a3a3c39021a95fa4a0a40f6d9e679464381ab22be7908f328b1a5656957383fc22f9f0840f583e74f9a0dbb93bf3eb51c9ca9db1e79c73e39cbbbb524607c2da0b5a33fb8a31139ac6098459739c124ae0823f40e5ac18a98d4308eafc45f2a18637800f3e1fce7cf4992054ca99369e5d3203d5f1ec362e76183b0edfb4dc052406100e66c85001081047d8ab073100f68218c034408080bd0620a87310040dc224fd9c91451d234c21c4121858c4e1344313258c20ced6c7983a414a290550e52f129016432abf2a0b8b97d27dc1904a70a6ce9794605873057735340aa0a30f5d4a41ac840d69a7950b2e8c08864b707ecec99ae2db6bb6c0e21b25eb8ef2b3e90622e2faac07f28b0454e57bfc22f5f024c508a377dc4725c7b9d69582db06d2c9929b95e8561b62d5791e874fbec39d5f7983e1e6332f650f112ae5dd397514b8deb18742a13a507e777777777777b7bff72991de9b3f73ceb11ea24ee59c0eb72857129df30e0576945316f092a8d1f73bf7cebbae03bb18398ea334721c9d1690dfc515c27114e5027fd7711c8feadb94ce72509e7a7cba791490ed318aca72cedf465b9b6fddd7dac01a7609d52ae3a8e1f47d56c2916eee65f140183c199106b624c04ade798b6ef9466a1dcf39e793f99bc7a39fb3a1272127a0a6f8c3c474d49414ee12cc715c216c32f3d47c7bc0f74fe90cb6007b2591478bd2080528404193264d54a00215b8e0820b50a04071e2c40993c92b26d3234b6632a7641e24c77a3666269a3d651c9b8c274f9e84304866e6c8ccab14541758324b3a7b7a136efc6a32186acea02787a8d2956852e3be70032947374ddbbe0d0cd935d7a22c1250d1722441d7ed26e9967be537674a0b4bf8f56dd3529493359dd2f196736a1bd5a8b4f1d6d9b536ec5a4ddb369ad2d98f4be9a050a830c84ffe09eeee76e4a171bb32bf507e0c2b03643c8c2ed497f1b03f469007959e2fdfa4bce3bc53c555ea4b51c9397b7599cfc993ea0d51a3f785ded398989818afd65a57abd54ae59d77aa2f6a2995a78aa98f73efdb25e1e36a4739f084e9d2739a7eedf39372c67338cb4c8332bc75d1c929d967fbfe9c735345e4c6bc499e3fb97b7a0e73d7a0932ea5d87c1794a92651f60f0d4c5d242b5e3ef4e2e6ecdc12d995311a18c54f70865dd16d38e2a05b6da92e120f3e4f977299c50086ed14f4d83551a05a962a5d493b75cdb7467987fa42ea5c0a8cfa42ea1c4863a460c854fba4cde4243a1492bcdc4dd334ba94eefafa6c8eb23f54f62804f4235b4a70f352a3a321638c31c6cee1e698d25aa5ecb8cfa392f31b4e7293d3386e636dca8ed56983382358bbe9f7cc9ced73f6f67cf29af7945dd2c55897ebd34329d7e5ba123933c339e7da7064ea64b7a25b7f2c594a2addaa72ac8793bebeb954e23f88a694beaf494d6ab24d49a94949a5d424953ea5dc99a914e46bd2670b344df3648cde4ad8c1d6e20ae1d8ac31735c5733cbcd88ce3725ecda0f2f89cabe2f993d85b94234163c7b5cf1ec1974f2a4bef4b92450289db2809ceb92cea9e0a55ca96ad24f41b942640b5eee3c7bec303a273cfbce51de754e9da7038f48e7bcebc017a8a79c035bddce0b4447a8a7fc05ce555dc783d37d4690f40be5f2535aa52e7afead4551d73c0352bac6b9d4fc05e95446f77376a15cfb5cfd4239fd50f3a9c694911e09b6d6a5f3c838b0b54738b0b5d28fb4166ca9a0b9f423d337dfbe17524e5d7ed45f50b554e05ce51ca09e92817395cb403d25eb525f6b7d65aaafb5601276a17c65338e1abfe5b9f495b53ce919e05c527f413ae719a09ef214f8421be9e9a931ae521951c52376a192f40be5321f937ea13ce68bb37ea1bca27c857206aa31895d28f7c0c8855d28944b39ea7a188f1e259d73249d65e35f1b9496d4714bc68ea463ad8ebee00beb713d82ad7e21ba045bdd2b7baad1d7e3520b384b70705ac071613e99fe94c6990b93fef08e902dd8bc1229c3cd29e71dfdbc20549b97bff9729f7c492de042fc08d2166c5e0395cc13262875e8e6d2b96dee04f61bd864f5d4672b85289483a7ee04f6147441f6f46e869a5376cdcde536956c9376ae7d28d738d7a84fea93faa47452d9649d66b37f22f39e773ff1de63a27d4e9a3ce07985447fa2bde64e347fa24900044fbd0990a7ae7d4d1ef07185449f00cf537037c8e624a23e27cc49aa6daac83e892fdd666a3178c10920900002b262063db002873776d022ca8c8920263cb05e778538a288819213445504d9e0073d60e1c40c9cf8a28322a20facd7f5404c8c1cbeb0030b2f8ed8010438183dd1c41636ec884b7440003fa8e1a78a272a4360c1b3c40f10501871a921065e94f000cf0c49e0b004972dd020238d2368186369498629d0a84112bb2ba43046186100614b1419e4b0714c010717622069616207487860bd6e07e078238b1a3788b27485cf3e91010510aa1035cde06768c3e0810e292ec8420648080131061153eae8a2258b24b27c80053a9c90bcc85145195dc87878e8c1082c37e8808a133738c203351dd8b8bb82d350d1ed12e1f5402cd2319a03031ea4b1850d68c8604516bb20438d296cc4008c1c46b218a3111f61b18aeea1d65a5dee364f3db6b187eadffa12e96ff72203781681a86190d095d848e3c8dec284180c494a87599800dff4f450fb26c05f118f9d640477d4d62fd20e3d3f01138ab4c3d82995bdd18d7a4ecb140577c768c28b0de0b0820b35c8e8d409311b0c3f4e7589ecb7c76cd7c58f4be96cde812d14f6a3f3bcf685f435289a7f9cc6f3ccb1d0319811101358b820065db8216b67336a18e4db839cc04fc12631cccf2c357ad7f86de093e94506ac93d7048bf58a44623e7acc4c0d810059214fd4e81d8b6f2291d5b36c7a83518d1ad6ef1767d97e0dd523ba6d1b6a28bdf376f6acd832f49af3b78ff27073cdb7f84e9ec84779cabbeda417414f6bf9759cc7290a44ce233318aae051cf838410610ace5fe4a9e21bdc0e157693f81bd215375e024f242025bb5078125f9be017c2f0cdcf370cfda652e06f7f1e549c34ec40d334d7babb5b6bb91a18d6d7b8b99bbba7a6b1767777bb43d8f7b6d2253734ea3ed222fd7e38c24b0f41464dd3b4a8691bc20af9581cd69915f2922c31588b31f627e532a1a6f4604b0a05ffa97d4ee4ec9bec4409b3200afaa32b991fc72e3d7ef1bb04a64ba8740c17c5418d2a54340200000002c314000028100c088422d16834d4d5ed0314000d88a8426e4c994ac42889510619638c218410000000400066666834200b7712133ca5ea8871b595c2851c1ab70474c9f8c8ed29a59f10971cb6a1a30690d6dc14f3de4658c446feed902f5d33884445b4ae86a5f936d5d07d5f42651f15e2ab360b385a7c9c213077ffaa5b86e9a5516ddd56fe2e1f131aedfa323c0764adc2e2366f41500ba33fa34e6b4cd94d3d9173b782280358f3a0ed6f2c2081e3a6c168016dbec404e74017b9c74b40cf2e1b80648716273e20b6a53763f10cd3d2cd475f635a68d42923028bacdbe73cc4333e135d0bea44485a67ad865eb6aed838c823e30be7343eb297d830658ebd0637218f0bfc1ffc78c723a142bcb41bb42a2f3e5835cf116436f14bbc8a80e033f5b2af3a326b1fb52808bf886d97b9ac41e5831586326b1c4356a9f2f99573bad8835edb06cb478685b9ea29f32712610a590a60f6a7367712abf2d3cfa56ea7f54355be73858e73397e967396ce15d6e0e2975aeb998e1cc99e04fcb17ac827c636ac4c774f527fe8571886f3a754e65638e024cf4a4572b7be9cf10fe6f3d92716f306f903042d9009a39b2a5143752a96aca414750a458a1306c495c302fd02c9ba5f65216511c89d5bb46c523cda6cde7b5de32ed002ab8be15de25b587f9ac78506bd1405bceca70003d499874c9fac9144bd2c0c06fb5cd27a7019efbbe5b04c5a99b8182e97cf13148a1865321397d7d7ceb6d572adb1a48f293731c6974ec7c9a49d26a3fe8a5888f7f81a9b8b81516076dff7d92fb2f55cfc2f65e567f5ba65151efc7d35c4efe55ca8827f77e49777609858b46c90520b881a0e8b08d88859544d64c6432f5e36646400b0a978d8c63c8829b8c983c17b381cf42b182d0370218ce52dc5d0eda317dfa0cba4d7bc96bf0c66f36f6af9b231df49b73edc1b267eb90265d2dce58ba48a3702141f3fde113d2995de1a331e39701ed8a595a499dfbfd207fcee048222903c162b13c350ce8c6bab8bf84a0ec15fdf20c3af7f8d187d2936fcfbab8d9f1ac2d53e2fee9ea0188117c16dba93db7956030c8adf36d8ad75739ad09fda1a232c49c9d8318a6967078ce12bd6289e3ce126e3a70fe97190c59cec69fa82c82abb6991f09a22dcd90b2094f98ad43761840183d4b663d66a11108ad2532c8f8fe2fa2a99cfd7d64394de54336ee322f5e539db6dcadb1d17c1e0f9f722ae84a4529b3d8ff2128763e9195bed02356d809adf08e2b581cb4efe32caab832d6de1d07235e062a2dcf3d6ec131abb291d02a2e803e3f5c4312031f455cfd0b677d63a83e7b3b8e6761485b22394e79c60e9d5818649919af855a4451e291a3adfa01dda2969f6cf679eddb8cc5784df65c48617e28cd0391405ebd4260d8f1f295820807f0dff6cb21c354857acbc3976e5adfd5e4d84c2584e14caa3ec11f51c00c22033f52bf86b7685d655c9ad97f31d2f4c56e088e3b40f7fef5f918f6b834558f5000d28fd20fb0b2b0fde7323a37636871174ee774c8c3dff384fc254cde301eb89c1eddeaa0d8a505fb3ca02125567b21cea706b5ed5320c124aa9d7c484f3286636f9e30da6d6772e094d6f6c6746e91fd256c9e24e3c675609b1a626049bfec83ce91703de99125642a6a5363915a1962492fe6e522a3fac914f25cca49cfd96733c619244bf6c68827ecc92a545af67ba2719fda2f365e2d0925c91f4019d1296e9caba15f75f61ed0960377ae8793963905e8e477b74e00d57a9edad19f3b5bafd861eb7226b6f8e3e4d969e2b82fdc08da56b88bdd48339b41ea7281953f5bf9298b8b9414676248cce3f0ea9497b541ab3ce37a56bb66524f2db3152183d3bf6debdcec0620d5ac0cb63ffe28c8e956d25bf4901f42e7d22a055836ec79e9eb026fb3108e4c751bf2dfb20ef8b9e619866bb2a51a276bfaf753c804a3c010a9970f7bd04c370eba47c6b642bbb923612f84e3803a792b551178fad0e8190d621c9c5e3058a5a807f5013f3357863ea8c8b6a9880ab80732e16256c9b2e3c416dc5fd1f558881cde19c73e16f3109a54e50a43890eac2e8ada0fd6a035ea08b3ab9cbdc5755c0d2e58b803d0bbdb5523b5cf03e9611c45282fcabfb532a3d3050f48371b5375c09182c6709791a2c97e0830b43a22aac71f8a14c4759c239e98a07f6d22f4be80dc8ed7ccf6202e73c01cab8cdc285231c13f6151c98e7ed6c0621e666e0b63f7c96f5186c6c236161fbf9704aa484130282783e23a6d4aa190c8e7e5ab9bcda19b8d5b0bfcce5a957246d654098c98d729c7ea83e5c300699df0b19d2484b9d411ee35c0ff1e2ceed342c6c348ce65fc8586a8cb50472a184a2615951269b5e021adc9bbc7a2062f1c6e1ef2f920250ba749b1f784fde8416985dbfc02a8fe4c3e4789d42f60cf36e0644a42edd985fb6af40dc4d82528db4d4cd5c148418282fd9b8e37ec60a7fa2e0555299d1a1f03fe41ab5bff920d7597a0c3e098e184385daf5cf2915ffb9d898e6800a41911890b0b0e86085d6d91fd2548e8ea1e42b2e5d901236cd1b0fa0a08d346ef7c0609c1081713203a080b560a0ecf24e0a05ea84d1c3f9332be07b9e557d8a49f19f5a0cc0529008f5df574eaea3ed23046c2bb1b8800dce60ddddc8e42a72e78ee2f6c594292710e8e62a02e96c506ed9ccf274c902240e04c73f5cc3973f0f9e7d6ca668b60224b7c8208f92646cb0a759bb22373e2fe21c167b7be467c7cd1b288e8000113499ddcfee8426123c6cab47f1a322d873df5258b61ed78d024b15f8c9cb257af7d4189c8cd87b00aeebc1870af30630f27534a5541eed4e32df4ae7d19f6f0c555c2ee7e6b1847cb34ff2ace1d98c94fff3d6d71e758c4d33b2e94478732c79937e62dd10d1c14ddbec6d789b41386094196f4bc63e51feb1f7737ca322888fb6bf5a9f8184e8a064837f2344a3dd2c300f225ded8498fdf92bb5f2effcb2214a8995705711a3e12f21bb122385f870266f612d4485db6a2813cf4d106356033d5198b3c0f59bf999d0d6b53021b57a8f86b532474fb52ea8bacf7f368a98aa55f1b4c2565b2c24966e7338da645acaf5ebc0058d7497824763c88172e0c2e171486efdbb4bc1737b2862eb10cb624c4b50592510be1230ea391c215fe4ec8a46a8ba7a83ff4a82939e6008ae61b8598fa98cbf041c7a06457cf36ab480dc820f616930d13334e4dfa731eaaeee45d9b6c6235a08e8c886588c2aec5836f4c3208998aa466c3cc2ac9af60ec596ed800525f23f7f3e25f5cce6b3420de296122c2080fd922fe239982f9af7e96de789ce534c118403ce65cdce6fd5dc1cc01cc6f4bf3c5a1f132fb9f0bc5ec5e829998411e5cab88fc6d077d121e7bdbf958fb2e90d1aa457bc0a76e1cf25f2f73a00bc233320c15b7c2ab143f36a773fae28ff6fae9833da3804333c1124c0d9aee801198ce251d942b5a4591601e5efb6ccfbd4bbb3aacde567298a00def2b4c591e9f29162e2daa0f425dd1ab782e99a204772aabd4e54761b744016ba377ef66f32ae8c3b1e913ed208da78f20540fc1dffd7e6cbf21c520d1846e49798668033c2878ad539fc014bdf47db8c31a899e91b4f062f8e9d1349879d7f65f150ecbe63666c09e5a5185dd71d18a42cc71154cc713171b9235edcc82144ebd06d5c74c2940c9c1868263af923328bdb15f231774046f5a601eed293c00ad5c3a939b235a5c1f03892c5aa7922e8b538ba1c32228bead6fb22455725d86982f95a28b2c620409529245653171fdbb122b31c27d00e2e2d999d9b8f4bafed0abf50a0d9f7f1bd2ed9e3126ae8262844b2fd9b0861a169883a5424719796520b866104832917a4a2358e082d4ba922c6de7ebe8a69efebaa8a6a730c7dc303bbbc891d6f830d4a790687683c35f6ebe645d119e8689e3a0ed31a03c46d778724f7e89ba0fcc5e1330a392c69b17289e2e61143a85b70f37973ac5474260df640a099e9bc4b42f30a1bd09c1eb4f0f6a44751dc7d787b012d5cf896f46c7bd73717209fa1735fc6110577689eb08cdcbaa3ce684cf874705b0578f2bd9a5c6a4ba512a01181bb3628403a4a2cbe41d5a7b2bcd62d86aa2367e7f9a7d3a4756be5ff1b265b703c623a403802b567a3432727da22a13fba098ec21a2b1eff9f876908800c05b7a5127fedde9720928b23d4b4a0afb36154572504e235b4323db4c7c069d96ff59619413298827f02755f0eb4ba64c99855dd2401d0ff79f7379944a3582d722184be84c5a6a3885b721d221fdc4a8b4db6b2d3c3d22d6bde4e21229e5e22505975529c601d9a00d7b095721f79a49017c2302a5be9ad0b51844e86238a3efdb4d991449968042197a6a7d0894272a4b5aa8152e9aa84caa6ab3fb51827867d1059a2e52aa2d09d5e218dee8b8e5d3aa91b3c13951fda3cb9a7632690e83dbd50430271673b744316337b4663701e186621ae99ec9b70520b74c60e5411692017e9126a8459d840f5447dba25395a974e59f7e808b1d4845d37217d62d52dddaf9ae8046442b76e08275696a6ac7574c26e7937702dd07c833fb12e9c4b1b8424d3dcf72e1a0247b5f70b8d5c689543940d73f8e91a95e745040ff006dfb86fbc3a83ff62ee4099f84e5c0b272363429c54efeda5e343fb2d4624945ee62ec57d72aa7ea55493377501cf82261c7e383b75e1624a58774f6f81c032a498e48610ae7121f4d215fc41a1f97286c40213d65ecf447bc9536e9d56b628e1eb66fdad409bcf1fdb2f49db31eb4d45bd0be9921f42e9bdfec75466b361a0bacbe5618fa74f1af321aefee593bfb020e45e7db62f0426ae042ea4cdab3bfc00f8f81c737779ff4f5944afb0e913e5fce5b79b6d0891cecb8aaa96c8c22ce23af38f2ac55025e2cf40ae22bf4d7e48f478d085e4a8ab4dc30d5268d5964e1df87c3a2d9efdde9da5d0bfbf0a150206dd95c527bd597c99a8159863e437d0785b8a206f15ab68e85cda8b6e25c52a382c90380143e168c6739b899e1b4ac99eb31fa97d5add59fa829e73885c5119aaa153a0a6a09ea3051d6bbbaeb28451fcd82ce519f69cba94da94c6b0821c20e05cc1d45c1f0494c332506ac72eb9708990dbbad63ec45eceaf8c83b5d58d61c3805a726522e36ce830fffd8293dd9e9274f1e8543f8b38884c7f7ae412333712eb97ee48e652eb17e7d4c7a4382700c1c8393b6a70799d8ca5e41ccd163f44ce1be3d4037097e65f05dd639a078bb69aafc0280c513c8dc9c170ced8e83fb91de5ced41f0a07e998a61b749db9efd39483156169d4d4cfa50887b42d84363903d1b27eebb320700120eb8295010b8f1f6b77932fc0416fc87953a91046341818c2b3903070a4517ee586aa31ab9768f5c7a347c006a1f9e3fe0b201d921fe88eeece22945f61bce6adf6e2431db8abf7b58eb5c2a09c9a1a5b6a1ff910fa32fa74daa0f548384377f4b8b9b9efbbc8ecb57dc0cbd7b536ee100f5b4feb1d11283c9e635791b4be028cac2ef821f901c7a08dc40c23042f27dc5f3a4a5f7c4751e9e90c0d0a6be36c2bde385c0bd4e18b9484f3a03de830457962958f4c749b8ece14c6181621094fe21a0302b1dd5ad5653b8d043016b257108f8e9ee485241e8419e49733eb78018aff00a42890f25b5e1ec0a4d248d4f373b86753e6d2e34cd6aa3afa62645232c9e9cdd1dee6eba6f088a3cf7b048962bc510d72b52cf419a626283dc78dc2885f963fd7b5dfd0a1a317dc41800fda0826c3394a990d21746a6648acc7ac734180562e390f12e7d27594ee59f9308a3989824e0cea47a13652efa2dbc9d85c924be27e5a092816c027ff6183fd793c60cbe78897e1cfe360842bfa6c9f10fbeb8539cb524d357691ccb450a4c87366704c1d1a8016d2c2ab480ab5f77053ea023471456bbcd530d296c5d1711c9129c438989d192219a55de85ec19e1ad1b6765a32d81ed44b38742df9f3421fe37ae5bd8088703893f5536ea88ba1b5ad2d6292afc61d7bc5e9ed4d63166cdfda88ea95fad32ec1689f196f0b6802e09c7ad64eafc5758e16c24c1c4c6ee2980cb874705a061093c6ecfc97b08ce77ab11dacd1d76f7be023758b3b43a651137cf0ae903ad147218ffaa9122401f1e00253ccbbcddb04c4b7905f224a51db593ffcd50d6284612cf88ba65712a5e9be7e2cb8e5cab54f4e3b0ccf784e58462c88441b2853ef57ebbd31e962738bb59b3c4a930deb215485e7f3cdf247e412fd0f1a0d575aa71008c2cac039a380c7dd5260b5917da820dabe5ec669fd6774f4dd0881eac0944615c3d68cc8591ebbd33778d04ae3ce6f3287d3773151c1a99131c8504383e9b1f645ca4aeae5c352b589a3bae1b04185732f5bcc0e25e1d8e062c59ab85bd809116236735e751a32f1b13bb96f06c4e80cb946be5b2c43ae7711614fd5a91fd62874fa19c77e38ecdbccede308303789e1f9731547cfaebb676be004f1408de2fd436d630403d716fe7cb66ba4d52ee607d9854ae9f5a5612255e08d5a6cd5aec2b9746770ca92614c16ae1a689374f8596f87f309912d413c5f677fda6ab375672dc71901b6c77f17224d66d641d5e78342d71961753d5470d358b6ff54077f206ef1bcaef132187bd806774904e84bbdc5c8477eb4e63fb5a94673b0083bbe93145480d855af627aaec9c18dddc2eefb5d929290767785c1d37e2029dce11242f00fc8dc91d96e192c5003f622cdda2bd4e92b5c7f98d38294516b8c76ee42c190e2c5b11be12227a96fe21eac0c50e95c94cca6d4c0e42faee43cb3169b5999f9103cc2c0c2a4b25b1cbce67379f19e9c8eb05836cc3710ab54c40a784ceb3b733f3462cdc6e171e37c00ac669383bb41e0c0accf6271d85e924f5890588c09b74411da0b6bfd760b1511a67afc64c68a235a8c868d6dc6d72cee33b15e60bb655f7d364294b6080722c14d203d187a7bfb4a4eba805776ee71e787889982e43d324b8699b0781b81254f85d59570224b31a37f357b20c2ac95ab5710a37e7d47367409202d9ca6605006cb83b10edafda570e0530f266191ed26e878cfad4526bb938e4981f5145413de6d072a5c855f47171eb8c6d7be1443c9ead134ac67983c29bc29bd409a85a5d1840809ad510a7a6727d2f4c16488c78dc795314d5aa4a883503c9d0362785600fb15feb87c36e7bd6176650878b1b5b68f72db28cf6c997be4049223ecc9ee94308a086b881a11aeec49440ddfe2459622fa1cb40c3c78e8a4508dba2d5b8c1b26e32c02c33f7313d089b836e8a1f8bd7c303fd2b3520a16f3e430dbc9f0992647472b025e5c64a4571ce5b58174e6128deee01c00a9bf29980450fb8f4d898d367c805f03ba9b9367ca50239263a9e86e36df944fa884c8243cf1584a188fef0b13da3afe12b20d41c8dcffdd650c0c105381dc9e3b5072426b4bd8732b7b6c017900b3885528df6978f55b1b8fa5f93338602f36b1d392675407a3afada894c65a8c2bc858aa1f8e356752adb41f313a03a42c44cb95245c47526a0cfa983956b2df4219860a1f78c56758d5e5a2f38407588c0c0ea10d42f1355fd64c99b3872bf2008af84b2204b0ef583544643bc80a903a42a4b5e1c87569b744c694025f897c56ad177b2e31dca2cedee5d633264c21cd44501fe95aa68fd0c8674844b2da7fc72e4c2ebb6123ba7639bb0290a07718da0873bf53d59196a34b70ae350f166790dfda677783697d0b9286b6773d885696a444e1ef27fcb0467f64dae8f298888de0cfd9384f1f811f803b20b9b7e242f638706f26f988e8d0a0a64fbec4dc3b5ecf115a6fea253fe360f7b67728b5f79e207705f53f09104e6207110440880634f254252bc8be0a21946bb5d2c630ba89e0b7ae000aaed68593dc1a476f785ff8fc0241ebedc03b81a00b4df49144544e7721642efc2ec93345e2e28c8e39d62d5dc117503aa77f3f78a587ac6f6da4ba1dd04e0e1cde55a50555ddaf8043f828abb2c717607f9af679ea555ce3a9bde6faba300327c5158d56e0ea80d5e7825efb7c7f31df3fb14c4ddce125bf000826faee700ac7d85962106c4229e20b19212fa5e161ea3a04e85b3fc0b39c7ba3371fc560777597c4e94e6dc8ff5083c10dffe763daee77a5537a4872e95fa84680db5059dbdbf40939c69356fd0ec8a651b43202bdbfc9510dba844e7ea0febf1076a01051fde5601afe9719140df30ff20d63bede2059774b903c1f4764dca04b84e93ecc43ee74788a53e0296bc018544de61afb80d7b26073ff0ad608d9aff885c022d2d5973b32f152ef40625992ff98a0271905e0dee14465ede0b1b001bcdf04300d42a65b20efeb0fdd4c13d53dcd0a43aa6d08cc4c7ed270cad29c6820e6759aaa78fb17f9837b60f79b2801792908c9f7dd56acb49532a28b9bc562104b751a8c2e5baaca9d765be319459ecf8cbc4f4fe7183daa93458256a21411b10d1a2e2e2efb55b450f5613fee2d4647d22ea1761684ca9285447b3e4ae55ca1a2528415b781522e4184611562458e294f0e67945fa4697ea32916e937e28c338d9323a481138f7d96a6b71ea681750810d18350d3ff6408e264ecd5c7559f586a30fd33d14452d8696066f7d6559ff4347f4826336289a519d6f822410b260ccec584551e1f4c79628cc05ee4c52f0246255a83caad4f32ac0e147c83b9f4a85cd39abf1a2fc6ab48c81de9518808d896d0a24730f3f34754d1faa7a90fcd4e84660338681e214865a84ac391fc84dc8d6c60098f731a71422d38bfd05cb3b2c1c3504bd93f16c1b25c6a8c771ab841761016098aea7a040b9d5dba8c132cbc3f683441724ce23f68864b24dad7c8ff8de486b39921c7a052ee224206b06310e770b7caf078a8064a0782f4c7dfd40a1f2e114ecfaefcd7838f10b4b32473b5092940a823efc605a503d2a236d3586d78ff12ba5ee9c504de81143c6778e9a1bbbbc105684fa85a411d5312e9442416548df55bf78b17dbf52034c55e0557dbba554dec4c70240e0ccc02871a8a4ebdda422e2302f5b02c862960dac897a751117e4a3e8db78ff0b37253c99ec2b7ba5ea5648cc718a780d38e54370bed84381049570a8810843701677319b84895dbc1b90043b6deee48bd715707df168b451444919f26b2817640f0882a9347782232d50a286a0eeb115f80722c1cb4320e75b1759268c559a0c26b5397225e886a61b8f30bf07e468e449076eaaf4ee6e13d9cc14c98de77f52e20d2a7740df0c8b281ecd4cf8db67fe29234e371b01aaca9752129061ca2f1843fa758425aecce226bb902ff995ee1a2415877dc4cb27e0278d46e8770e803a7c9e2401301524c09428415cab317305a0f106bb184024eeeaea5f41ced6677f8a583e37c8de709035f7d17e017a3854153f4695752a81cc67f85ef7ed9ba24806a0df88fff15672494334a03e5f82bf50427aa850de4e6707bb2f9df6774f69bdba11b5a31b532c2c9b808db9e38d4ff3a8afd62af8552e25989686d34115c2cc198b2b5f620808c47946fecc59ba81337b7d304144015de3a76fc099c6a10f0122263d907f03c82582448257dfc72e7a9be526ade6841ed357997ae67eab1636feafd8d2478a3c1628e7261ede2786b53adb748eb55619039731a1d3ed6a9590d6e328acc35080b62afa582e3e8fcb02e86ba220a707db97c1e0b2d924fadcb25a3a479dd9ab616bb99c6c4fe4549537829ac5cf5feb1768634dfb97d4c3a1691b6d76f719d1ee28dcab94583e6f8984e263a87d214865acc79b8b1356195f43a2acdbc10c6523eff0ff81102ee75697097a6b91563bf741af02309d42a39bc2dac32944fd1a2114818f733e7a6bc275b21f71cc88835a7145aa5e316647f850936b8cdd2145d13fceecbf1345f07bfa8b1c0a9fed446aa8fb3926caa96b07e5c3f1b0d5ffda1bf46a7bfd959ef5dba76e54a75d9b2f8b3b407efdb91ef207eb8aed28959c5bc28a4e8fab3c33797682746c6c6dd1ae8f58c2c33f4fe63625a3470b8692d87fdb5e66c2d742975c78006737bd87cfd45ceacf88679bd69e3d6f9f767d1b473d897a9797f9044edb1e7d726b6ecb68d12292e177894c2245634688b375fd6f84deba14af24911da031341c8ed3e8db8dc76e3193af64e9703d531315e3d95bdd96b39eba1653c2d189c4903b3c6936076a39c609e939a80a804940c96a45fb72dbf37b223427a2c1b9b7ce7efbddebcc0f9c9fae71955fe0a223fb1eb34fb7dac549898bcae9a97ee066adb584f5470886068db6e03f8c1d0b04dc4072335d6e3d50c61f53510634074eb2811e9f551551f6b2e249e3152d83b2828f697dbe25c3e9d045fd2c348bd11b2be14d637d425c39b4425e1624635b5e948d4e0396a878632233835b19365cde5531fa25e6ed2b03f6400bb286c95aec26565c0f81511a516555503700f184e04bfb6ed7aeba9eef7befbea992c0e72154bc88123c0978601ddebe1cc4b54d4dfca8a35e5dbadc1307efee23b26894370b40c9223792340b25519e0ebc597c1da643a8f0b05650f1a82a0bbd9eced4f6e13ca6a52952677474b34f0dc473156d8c8b994b213edff0eb507274485089433dd001c7c2204eb45928eaf4c50fa7929da66940e4071e8bb81d7990a502f7680e67a6ddd565f4bcea0693cece3b4db18c07c43849fed65e34f3f1b8eb69f7c6fc3b872934101a3834ca7fae68f14e960454ec13b135836b00c5fee3cfc9052167ab95186993796178dbef5b30d702d42e757153bbfe0436f7abfc8e54caec1dfea02fbeddd961c5cd74c6ad0ac2e485c40832187de72fc3a102f4a26828f72aae8c282e54b09099360a2790ab098260d868d35648ef092b2d56f06f3e0b1b5d0fb9181276df1c5a5914d4e7158621e5edb16f1ecf85b5043081a04859218bb9126e2411367d529ec71d511017282474f27db8e78b220c07a4cd06264b30d3af1c8dc27bb8866cc22606120ba8fc32e0400af57695d41e1ec44b80f8a142a2e1cdaab6b894a62910361a2a0de0251bb502e4d89252efa87eefe8246312f31412dcd78ae2fe4e6115f32f49df893815d9201035fc4bad0630fad9c357984c4cc165d26d36bfcb388d1002734322d02e7e80ad21a413a19d73d1d46b1f65fe91756764b0607387085dc60f0030685a8ad85812c360344c290983e330d9308d0f1847f09204d97dc12afdbf00256003f30befd575485940b167f5811f2a43c630599fefeb6e3bc483a1872de13426d3d595c775cbd7b625b38611780856a385876db4b85a0e91368ef52ac02e65c7dab56da0a7a91616afaebe4838eb8428aead43b26589bd0d9f0bd64bea688cb2d7794cee916ca1f7ef807be93a6b29eba1e34f402efce22279513a248e263ecbe15bec014cddf4e7decbe30a9369613611167c541f9111d079e12cdbb8920f712a9f4b45c2aac67f21d713f662c59c4ac960b8c6fc2ae84125f57e832b1cce8a3de7c2c319c063bf0030c955341deb318012d3382e1aebfd01d1e2769dda8de97c318fdf8136fcf3e48479fbc7b17eb66bfe845eeab49817e050e496e014ff44949503a65513778469214a01175204d98a491241798152cc12b19a486b83bff2c26520e078b811a8435b09e391eed45858c316a29b11aa9c822d74320f5c895e99582f81a09cca446b50521c6ce5197ef57c5fa5b0592d9834c572b5bc6b3588756822ea214ec6f4a8dce59ad026651cd9dd51f91d4d6ea4fff9bd00579a1f6853a56bd2637d363e916501afc0a8de85e1cf6367264f1fe9252303c889708341764a874ba5c202d1e261b6c8a2826aa458ed7d18896ed5632760adbc9f7e8b5e570befdaea164bda37011d2676fbe0294f29a73408c4551320995ca8644ade70f0cbe72148e8a28f397ad92502f6ecacbad72462391a794fca26e4ec3e6acb3f9c27dfcf6e884eab86ed013ceb7c1d66ea64b5c4448ec0b86a9bc992c812a2f1149b78b3bca562deb029654691a9e9497a298c9a2bd65250c1780d751f177cdace995ad29621dd8b74e040d066071abbe5ae5c24a06ba282fe0904cc8cb2d966f6e5a238956f16a40ab553bab645efee57ec61929f4a31e595183edf1c5fc92c205d4675437741ef133e69e23314ddfdb5cf08b5d5afa143173c3789c8018350011649129e1d80b69ab15337e8173bd4409a978904904dda60d9488fd1c89ef55445eab4762d0b96ea028fd6626787c1c3db73bf02c11fb2eef7cfb70f27d41822f27b7c36f750e2c5049d834638ae2e90596e0b88919e318c73b61cfbe2544c58907bdf8a680593aa1ff15f60030751c43f83e601aa890e091d5469be9e12c5f3f54abab36ec57627ddc2fc97a9f3edbaa431deab958f5b01639424425d4890cf48e5f84c2110e01d2611a730183630f4b88b4d273d7dc5208854d649280982c3bdec693923990fe216ffd47e40b2531a9072a590083c6c69ba80d15698d68f5c260dfe2e3eeba5691d2644c93a7f6b183d10aca9a283d997bf4ec1e3bcca73100770323a42fd098d80b29fdb3c0e2c1f72369ace612eb144f27d122011a6f5c614f83bd3f775caf8846d4f737285ccc396265e3d03a604f183ead662b27e03509bca0b5054e8bec45c369b4b55a1600bd632b4d3ad32123df515f5dc7316e0f51ef8524595df45c8242af19809616c7ce1cf97bb83a7fc43b3cd68324cf0fdd2fbb80b5273a61e9895ba1a17d019db85f572a61c30bab3c2fd795763c8f3b803db8c46e3c39d90c4db1b45be21ff92dc947a1c3434978744bb89772c84ec6e91af8c48732f6a2ab1522c57d6351d4515a088a9d9fddf6c1731d7a7537b7e42226f8ee78f8d0c69c9411846fedea28af44188c2a4ff57f6aed7d4e53820fc3f349acc2007da636a4415c0db71202bbce46e23b89a237659c758a856ba2d2f676a47869390dbe244dc3feff92e8fb9932e5867ed8492d9d7d3f64984492b6a93e167f8108dcfa872ef7b7689cfb6a41a1d393d8ffad8c2ddc7b87c9db074d21c66bfb3eac740a3da145c639d83218372cc8e486e83471182102200349cdbb90b41f49ae500c1401a6e08ab20ce35cf0e240c97e65441f4fd9dd7c9f290ca6f06c6c0b66cfd6f8d979337a42c729e08a4fa8de5fff76fce82d31855f0c531394c524955b5678b7e21caaaaea6944014d4d92e5ca72a331a9163e40041c346b415ee7827678be229d4e8c95762a90c2a3a7001451eb03021d741c4e94af0d5d897e14d7a41f719b7e96465865764d2088ef9c8fd0bf88676365c9a895854d1b7a2b188fe7cc255d83bd72f0c8e038a11147af36984a1315ea397a60f8543a01006a368eec8fe15556e1676161f51e0203e8c4380c45a90bd8c44b7aab34181b32a959b0deffe4df3265e8d7aa687f810ffb5b43e6766f787b5cdccf7b7a4fdf303e106e1f1da99194b7173e266c93d4a8dd2330bb89bbb5a0587c864ffe9f79d27307470e9bc0171cc8e702171bd982a13c245efd1ae88a2e2052f25b960d039d8a449d7c7e5874a099a7cdf8f3f9ac603da1e68933a7eb224722c6ad978ad5a5d4cfd69094aab476bc67bf3f458b5f17dc40b413d17a4217d352840aafce694838b86281a2e74db87c9bc89814d7d4449b16ff2100a34f805158ab3f12d0965f50ebd10d711779b3485746d29311ca91f8ff6e085a70d5cb98e0cc31bf0a4f7f506bb8fcbe3fa8e0337a43538c086608fadc19f9ee14bffcd9152e6c4c7b2b017bd849103a127bbde3072362adc2cc996f072c2d838fba8856f4e948862ab3933ab2cf401ec078895ac00b3fb20bdf3b13276fe9ae0460b3a97f8bffc9298d7cba45f08eac078830259469a8002922b0f20641c59232f6a11d4be527c1e695609e9948af18904107925e39a5245e3c2cc9ecdcf342ecb1be0aa83472dfab615d4e0ba21816d9d55cb7ec1ebee5e37e7ee0fb303ce21e910a219fdd45f7621f5605581cbffe8cf84959b2da4a30741f8f085ced654277a409be4cfd1daf26d53379f4c09680a6d122e47926c403fa2c4ffeab23dc422cc562e253231c5248d7f5253a4dcd888ba24cc6a4187248e903a32218613aa2d6fad81722d4c12ed91da9ae7a3a90f9e58f30e0206cc191338f873d9387dc670461c536bb183f9af927cd91754d7a3abe6c1ee987001aca1425e786331993ba339419d724feaa507b258e19600bcf82fa309cb5bd6e1026b137976f11c89615308d247c0815c2511ec49aca7183cd0b1faac508c0c78e43604f57c5bdede68924015484e1188aa5aeb795d76dab9f544d04ab6a7b61eea4e1721d29a8b5d5fab590352eecc479392b9f3027089c620ac70d397f5b144b130f1f80c084e8a15c07e2ebbaa2b067defdafa44840f734eda233bac36c181dc5219648c29f81829f7ecea48a25b6d09969ec84907ef32e2f05e7ee763fc138c37ee586592cef238cffc69c0f807d0bf1a6c9049cf75ee077d567261ee5ba22348043035c095960b23ecc8142ec102b379cbca5f2588dd6dda28fa23befe0582999a72b9f73147d7e4b4260b30f5d06132b233a424520d81628000f195f2dcc793db3b12b05304e574500b09f4fa5f072761f50b9b2a8ec3d375d4889a0c647221d0975dd0a5e9fdfa64b181951839745a2c3e4c064d80eeb81b6d359f8b24250496e3b169f1e69486b953f7725448918ed3e3daf71972d80e543d2cbaaf834c24b6f604671732a1d86140c732b2159565a0b36c63d3aff19f5c11e666e5169175e6041833f9a6bb2349be120683b693488197ac61962a07495db27b775061487b60558e59ff6b5e8ce12e005eaec4319e656a19f4ee2884ca03ac6a20f6626332c192c71fd08264808b170431b1a21ac42e87217da941b620609e7bc81068c2a0ff6376a4429e00ea11a66f06594a240c4c3a307b2f2326c97adbdfec5b3eb5f530d7072ad94cb2b3432876e190d3c3b67b91c97c0be2533e78d2144e779531aa95fd4075f7a1d4ab37c4a713f4a80e84c912517b4a911525b000c8e6ee012cf931a6d003b78c01cbf8292d1520cbb4148c8edd9b6884f36611a59655faaf24fea1c171af7382297e05b3ab50472ee22c472f9135f1793c324bc3b963405e4249b23e2105cd39eb85c78b6f8e0ec96ed7182951feb08607a7143ccba4d11ffda940ad84a12acad68ceed207999373877780874948441e58b28eef5cde1e4a10db97718d1623819dd17d802b621c84908e95f1da4c7e1486dbd7c537b34f0dcc7c4b1fe0e2199fb74ece00e9798e87b35469a14d3e487d7eb34f642634384a9095b8bb4bef47cfdb363ea2717e52d76f4c8cf929c24cb76b1d9f22346628bfb324805e0977bdf3e401da7ba36f839988d3bb3245905f04923186eda5e998ee1faaf64484ca62576df4e568e3024cd15ef022fe9b9d2c0fe8c834f72795efb80c4957af771ad58e5d98f85b16966518b95ed5f88da2e0931f86a5c194efedd2751aff7923c7638e75b30aae9a14cc7a95e8c1a613c9f4c49c5e49abdb1f283c2127478b9c43ac74b1d0fe270c4b525d9648251895779f7f35b22a38c3346c8f74213ea520af686c979b1ef287239701b2c9089b7a29a53363d460e8a670f6995c44b0baee7652582f5c34b3757c2c8a4985b8f8cd84d641a0a8059c4c99ec5527ffb801432613c4ec8048a41264e1d297732278f28d31912804ce48e9af36fd090687719ca0333ddd16bef675b661cfa1922b918249c0c8026f859b3c9de0cef7f7d783a7fe5a59e03f37aeaf8780558b24c68b83c2aca295d9f7425d748441b91532dda9c11e0bd2fcf01eabc24ed6dbb34c06039b75377010d08309c456bd19840d132cfe782e568b428e79e04a98be8e7268fe782d295a110ceca78a0f0b7e294c2c3c1a7781a0e6324c4971eb8f74e4a2e15e8ebd0f6e2b7836a35c820497968b36954268cd8ec45c9c97cbedbf6a8ebc51d247ecf133d1db496e9135e8332302741e2adfebde882c7a12c86b3953336458b438ba8c85d8ef6d2c626b882f955ea20b712d3c7feb0353e9c65116789f17d3d75a3d17185e9e0f2dde846f217998dd96ada2079dc9a98c705ffc201136fee3cde5d1233d0d573a1d9064031f242a9fb97fc0ea8e96afc43297318b7a1871f04e50f22e0657d24ddd2020bc4450807c0faada72edc2afa3f0ffe376d6b828d68bba5e310a8edda37fd02aa5277483c95ba041a2e1866b6984bc0e6e90fa5189cc5c710f3f55bb83dac52cb6ac5661a98b8affa4257598e7eda68687cb13434b3341cee0411cd51ba33875f6dbccc3e5c5d4ce955cbe1e49897dac6accdefaa5e5e998e8a358bd11796780bed736a0075c689c28a0f1468727489e176df8faab503bff6d1e79ce719649a028e0955447c3d3d06703a1d4050594d98478546a73fde642c81d3345c9f1ba91f8829d469362785a79ea4264f7551908314420ea22e61b3b501af02e561e1bf5cfed11eb49efec51ee07fb43fec4a060493014cdc3fcd15296bf881a11e5537646d0e2a74c4d4800e08716d6a0d674e5aa1355da8094ef2fdcaebc4af35ed0c1bf566b1c018571f18a5b82d5311f51b7883ff0d27df2e0ec21a89ec61518a442e329139fb082e2e1498e45795183bbc762cebcf1d11aba08438c4fff6ec8d6912f4b30feeda9549cb3e348a5919ed38e23b2182866b001f720b3114b2de81e631e9862bd3af6d40c99aa8ce74e38b4d949aae0fedddf1192e3d69ea3348355d257f8ffdf8c493121017589db77ad2c3b168e2c2c493901117acfeddc101598d788ff16a1062654d3cf95d7e87cb801a0a5617cc358ef029712449ba6c4b8dcf84df8c171eddf1cdc4d8f5b27b1b7550e006e1a56dd8679c9846aee63082970e782378d9cba825b8e9817844037011472678d00339dc802c4990ebec84b624a42131e11d34800bd60a78aab9790d165d469ba78ab4e7d795456ea7864418fd195783464dbc73c0801fef5674bcfe17e616af9674851c8ca1b88a67ecb545a60145553920eec380f62ca0c40c39d6999fb6ac240dae3ba4cdf776795e0ec4d503a147f8aa2f781d1805ada39566a10cfa89e7c47cb01bdfa13fd1176e4a898a8ee49df44557fce3209a24f3248cdae83f3e7a6531c3047d8717ba1f0946f1edc8d3f123eef8fd1635cf75f800c6e0229bce2a280577c840bba1e71668082895ae7edab4c73077a2b28012ea01772dea1cea850a2afa3d4685bf50e3faa93c089a1e2b007759358234faeada36427d3ded98747bb75e7983b61e70c7cc6a8e6a4c58f738fb5f068a24ea2de04a87fdc4ae2bf487163d4c39c9e96bfba92703634185327e19225bf38fb0fd7d1da3e9a083bc39dbe7fccd3dd9c017b7c03bed46f7377799307069dbf76b7c62ec6ae920f915a862b8ce07f483637000dd2aecdf9abe681fa49d49f26e4d93d675c27a3c787a5ef88d6196dd7f15d7a876681f5c7b004d8176e59b4c0078f507b05e40e3c8012939f8063a3d0607948a3b41d0f09709a0d090e92b08272d9471af25b8ee26ac8be814b9da1da32e9120b41888895569118b60ce49a3fbb2a5f1e3af8b9231ebe40ba397bd5e8355fa1261be6ef9daa8fc3bcf00cf58cdc32eeaab007338083c6e4f216002538dcefe065450bb5086b25d40e8986240b88bce6d9ca2f0b82d4d90a38d355eb3cc2100374b48a905f78bd4029d1f6ea0384478ca294524da9cc5902e22dcb75f907e84a1284976fd6a9f07ff1500f234fc169dc1c3314ca012e05b0e201eaf8a2e4e5088a47ec24651eafbc2b48a4763c8b7947c0d604456ca70a308a5796673c4279bf8ec8c86619529289e87938d568d14034b23c0464cd15d4f8959cf2d63255d74a008d444b70ad136fe1eecb8c9c8c43e4fc2e5fe56593972b1efa39469a3cdbaff908de605ae7458ad3e94aa8ce85c7f767f848b2290681122398abb84471a9044374d384bd540faf626ac4b547ae0f8e39eb1667b80b100b6f76213f68c7eae908773c0c3dafdf2d8d708cf2f34ea93ce512aef77e6e702993f6ebdcbbb291ef8b499e0278e9772c6c0a193f917f926fec1d7d62dddf7a22a81e54257a6fd9566f479d3ad264b744589687eb295cc89c38685ceb1fef8db5eb61686cdcf107dc13c32ca234af58d97753a7d201c91e9fcfaacf8e0ca60c620de68577c95426f46eef0372a125c9c8829172f1165b6117a0ee8e1d3e7aff791c7e1dce0a553c7e0fe4a0e71dde8218686d277937eebb1f394d25a7bc660ccaf1a8112de3f71255655bd7f1811e2484e6cced8a717c10ba2a1976c9526233efc3d080b091293b083efb6b0b7d04c54dbfeaa9eff9ade1bff49b1daa8fd9b09803de69fbe5abe21531f2c909bcdfe09474d39342a76c3acfa507a113b4345b6bd54e6d48a0f375f4f1ba8840dd6ce4224a28dcf6bbc50e88b85888feace22a20654c0ece3028aae8d3d6920284c2aa335e1e5e0969260ff86bd710967eff71e45ed1851a514e8fa595446ea654032504a106345d97ad12efe7764819e654ca5f4ddad868a3d098734527e06148d48acf5cadab2b6aa4444574542ae1449b00ab8b6e4220de8347680b3d8363368051178ece1e6e35f40e550ce3dfe78d9b19c8df3151f74b388044ca1aa0c951f4b1ec713d08ad0154e8672fcdb0f5ca0b81b23b7a1064f90e88ff9765a7289b48050f6458c09d1373aad91510b38b09ae2ac58c423af53ff698dd0cbf75acce0e3d14d8ab6d19fec1c92d2635666748f91fd9a1fd0c1454d5a0821c1b2eee040f91c22a610d98182bb80207bc2cb6e5ea108b8b44f020131e269942e2dbaf24a159f6d2f0b49ca8eb82ee227d8d15c9a1a3ab20d2dcb4feb5b2233c2af8b9ab555c733976666fc059b1fb39515a75a26c1d272ee5d173411a723b2e0a4b16fb5aabb656b4752f7705971d52296b9400c07908c01406bda1b4e5e21630a4631e71cca55ad9d35bb5a4cd41ac4774bce8ca76b08ebe2a455b1161719e292f945ac8caf54e0ec0aabe57bf9c74cceda24b0753034dc560ad11d9691b1cf9c86a9f3f3153f99092d77e9f1a880dad8799daa172199685322cc8ffc2bba2115ef7ee7adaa094e4f2fbc9b70a6de4e3d8f48fc3f8bdae12ef88664f710a436de7bcfd0bffc58863873205f25c225f5c5120334d08236c85aad2cdac2f01342b0ad4e250fa78fb0d6c3fd7f3e08d1c1e785b38952e6919bacdec4c490dd8052246b6da0387e8db2c600d2ee8c5a6740bd60ba3c89306ff46d4c497aebb64fd0018e7d94e153e013d5181727db668db8b87e90db063a251b61039bb4ed96612766742eef645af236b693b1a3af31168784e0d1e7fc2ad9de8e9c73c9cf907e6a6f38c3324cffbe036dea81704acd8339ed6664a4146fc309dd2c26c208ca2451910e8b759ff4d1b2caa4f3d04a53ee2608537953b626419e55c82443e1340b43f943dc2b0a35e94aa3b0193341cfbaaf310ff55735cee8f43aa7bf348820b264a123e9fed8d01b3317068c8a110bf7281f6081fadb4f5d1d676a983d455ea9c1cd1ff22bc60bf94826c89c45c0a7bfc47b7300d62d5d2b259d1fbd33728cbe75a0f404ee00e8a89b7e13c380abef06ecb618c97d589155aca75e1e590389fdc751d68ca534a4b5fbcb46ec60f7d2ef9575557452614c3e85cde9840c6686e938c842be404f5d413141c382c5c1506d16fe4e8e0bdec126c906d9373291db57e3678bc0021340084fb001c11370e6cd631b9f8e465b380e1efeed31db0cb82e77ee63f812b8e3063d88821ff21fef937b83388a939524bd5ff0d718c8ba5eac2e6352bab44b6550522e4892369ed32b0f82fcb2885085f322c921827ae294ce282b9a1f7422f670f6e5c20173109fe7ff9d55f596412d74452b794f365454722b0941d90f182bbc025140e00c4715692560644c480ebb69e1ff38b8b8abcdaeac5d1ed6340aea581e0ade3f2f3721ce015741b3696fc90caaa8f16025021252e72c544b4f244a50aac5956bd25cb197b6a64423ad3fdbbea6ea4558a91e6e9ad0c53580ac05bf8c27f441455789bbcc6bec039bc8c262ce6f462fee7813b181f4175ef0ec247424591536a2497d48df94198e1c6bd4b771a6f1ae7fc9aad425df187b32c00d45cf04928199fe94aa25b6a0b01c81301737f1041b0d6a3905604b81521fd79041c6cb174a321cde00153732f461adb08d17875e10ba2003042a13ec1f6b1073932210e28843a396baa979652bcf2ac10551759f30be1cd6b5ccd4187af289280bd39773566e04d901db92ea45292afbb9ac0d118831fb098848433cc2872a066fdeee7e492271f6522d9156b7a6d867a04690c83d531724ef8157b866c0b6c32b0a87466d2e7bd1a5db798db3bd242f19b7e2c3fa97c1a84e93a1f2896902c1ae9a1ff45609e269edb0c5fb7d9d454449435c6a2724c70a1ad17e49d787015ef30dc8afb8f8386cb324714c2fa62afead58d41515a0e1081533ed3d9c201f345a0342c473cda430dda7a1bf6d074c4e18945cab26f4f5064ee861eb387af3b4f0bf00e46c01681f418fe4ef9a674b1af2e33f9d3631e486b419d771b4518a8ef554b8daadcf1d6dc89387a956aae35fb70b7264aea5d3f4a7976fd4ea4c0135fa1384145d725bc54328cec3fb3012b92c2dbc924ef49dca8fb0cd8f5aec2c3fc2458d90623cfa64e5d80a3e672af682738b496d621d88d6fe90ab98b2a67380fbf94d89b183adce7759f0b9fc8f9b4b033775f614d39c9842268283c5d9a8c72f727cad9e75debd083ee078e2299bd06db9d7cbeb9fd956f6fa788f3117fd03efd2ab1ea9318a1cc763c15f1e451cebd19bda27e2b13cccd9c03617c3507d92c755fb69648051aa54124ce600fbb17eeca71fd3e0a9b7b1a8a8d07ebc361070a000269378d709285f475407d5f650c2a55b52cc88ec033952c7f9738c52e48cd99567e76fbd763458e1bf4e3f49f171216b10741ceb0f8ec102980a33667c54696a4bccd15dfb44f5bc7b17f9f721a4e1c73e8f7b2a3d42b39c34138106579eaeda974294b2089f5c35ca74968307112073abc16cebe501111bd44c50278e29d0fe16517befdccc0144630377e369be89501e0183bfb51f4bf2482bcd077f0b7b699816ce3f2a55cd1df4bb828f237b9e53d59ad58ab6601ab5f09e7f7c4cb460d6efe0eda6776d5f2adbe8529f617e3ac31a43e6dfd7f69b90f9f10d9dec62307f5d92de16ce1034c446dc2c31408687cf46629a29ae9e2f3b9a7e6950a625cfac5cc062914bcc911cd8bdf202bd2097b64f575a71129980029d42e81f3706f06293619713b631e270bc2919f3e46ebcdb3eecf519e6bafd8b685c6d861d9320aff5359fcee1cc3dc5f922f08bacc274a0c092bcea26c5986b32fb7e39f66f9c58e59070784c15f08387780b183ef648eed74abbfd732e463e7e4f073870aaab0d6cd5b6c45a5d8cfea57a3160720cee1b481ad4b8330f7a15ede0cf3f4feb4383fd044d5c0fea1a6c865741d0c82a78950e786d7251f0930923090c1a70b79c29a01136f3d2f78b67e6ded29c42af5f90b210a75a5d4ed30ade0841384d27400082c72585bba35d4387e0fb1eb512cb483c8a58fa970227127cf274b2a924a01d38b6dd24eb528f2a88785712662e493e398ae18009bb267cd6d50b5eed0120445e1857632df158af32447ea1dab1b3a9de1f39cb1bbcd8d0a912514fef48fabc584a9e70b62c4959434ae98333045f194093e9b4978e5c283cae849071905d9f2552cd9374c460b42117ed74b991686d306213c7aba098818df6e92e281cfea8d0809475fb5186f52358e10fbf20f711f6f6f4fcabd3c55aa703276c09fa05238203821dd466053264f93182a1e29763c603706b03a8960e8e88a6c1ec5edbab34417d8f3a0d0738a187df0243eb089b63f1ef4dee9db01008fd67568a91bede59496709dc7f7aff4f223ed78d1fc5c54da89c18b2a18e4fe36ac90bc868b900975fd8ea40717dd78a48f48e5c4ca6db3b7f0c0d122ed9d38b992279bdd569eee7feb9403c5582bcb011baeb1bb8e464172e730215c1dfd8b72c51e2f786e325bb470e3246b818a51d16786480e4c85ec938703b742a97921681464771f0070d49dc6f6d27f9de6909e6901c5ce0624641006e57196c3a83690c40dbe29c74354b2be41355ea2830f8a37fe25736c8d69ae34a116150e5f4f3a398037d045236d5e1a4811becbe941b39e8e3bb659991c7e81cae057b8792566a751a7b546ebe194d4843c789d9c268f8768b7a01505c178c332e110295839e2fb0229fc9458c6b219bcfc07a24171f04f9f3f989f249447ea19fbcb1318fd74ff1c49274e92793ba6774975ef9c59385760210ade13080d9d009662c7a41d170f3fd298d712b32e544599b77b098ecf4f40efa029866713b8fcca06c629b813d5186b074b951a1afbc4f914f2e64c75d7fa6fc7bd3f185c908c68a260cc78d0fd507a6872b24d0deca6543d733c63a6b31121b02ccc4ddbc9ccb0aefb84a7c15ebd99b00150b7d3390745c3a7d399705c1803618728dec8c83ebac3142ffe1e994481928de878b93f781a9080c999a50440ca080b8f8758039e62c45a0b02403463763268529defab3bd5ab5f77ccc17a6c7530e6514bf0c697fe79f5b496a3b7b3da49dd5e35453417f615f5fb0fffca50e9ca1676f9ca60b06702581e0e2ff797e4e60e25175213c13df17a7151fcb7e7156e5a9ecba9d0a4b29d01ff3cbbd8e61e14d62193d5813cba6e3c09102891e151fdbaf857010d355cf5d4ec70853af188a6a9c39bdc7c4b3100fba53fe6da8f40f0942633a657e74ed3f633b67e4e9cb91cf44b9bd0966803f283913f33b3eb9b4d6509ff0ef65653bf5744ea2ff16a6deb53503a7bdb8e1f62f42c2bfddb86eade0e663b96ff44493d34d57dbbe9d44fcd8f98e766164647ccb444a44f14da7066a53d8c9bdab1352385dd91bcf7df835108f7c9ec18b0bd1b0b75e88b8f710f71def527ef31c2ac36ca0825704bc63fe5632008f6ac078a45a75cc6c180b33ed027cef0030f7279d978df3ad913f06f4c3ac54dc7caa02d6c00cbf5b11ac67b6911f5adf2b61f9428345a7bee5c390a3c362a84f6dade6dde877e69043f0f43dea60e25a80b628d9ae25850f0977fee1bdf660f0548e18c3371d4964003923515628aa355ef916b3ce5b6535ce0ebfe3103b8edd7bb0e4de746ea479e0cb9cf6ddd37ecdb4a870077370cc5dbb90f14527c811a51c9beff5283b30b81aa7f19ab0a578151c3638f72ba950e4c0d87351f37e98e2358d917cfdc32925970439ab61df1830c130076a5ad3586458130884c937cd93a421d8bf1092fa89635f11123cc118bfbb2f7abdbff8187967ed3daa70f0ef99bf02a06355e49724c53cf98d91f044da27e1c32901cfca402a4b7a30e26009e912a38f2d28b5384cb373535395b99e1b602eb8cb36c1a913d2207b9c7a957dbf279ff28c5dcc353b93c75ae57e9ad4f05d7f41ce5cb653e9d2b2aead78561dd6278e029c646c767ce6f3f4dd5a169c89a2a1ae3e01748d22119a4bac914c818bb6bf08329aadb919e544e18027667d01a080f802c914bab87846fa47181435de01ffc729d4ddce0e9772ade3b50adfa0c4cbd3bcd06bf9f4084a5cf453eb786e8397eac67b255a99cf89fc29acff897ba4602eb43009c7d4d31b9f938d5a9002881fdd8f7eda344392163b32a2f3805a075f4641b9730ba99718eb036d0e26694deb7a77a01cf721599739b7a509abf1b31bb09307245cc552e0e706e20177e84382742b82faed6896ed95cf46e797d02f37b0cdf2c3f65c2594001c1404e5f00c9b8f7ada00129570d53dbf93e3bed946100310f507af0d4e8e68bf543e58be6b1b00722ef39e51dab53f7851b803a1839bf81ae3e8e3630140bd8ee08dff49e6c3af2d89a86117b668ce10f543dc9e19c4b6a93139045619e36bf6d6b091152844a58c427cb0c17feeb0e723e6831ceec3309c9a1f3525945d89e2e87e5287ee9602221ae55143d9689db7c6d3e01f4bffc4210260a1127b18a15afa57301229fb78e50b0210ea0094900533045c11fbfa29a0fda065f54ce88270a00c27f970c1d4a638944a0f3aa269d792005222509e10a059069726db11be132d76df393efe38d0c17a7f3d7cd32b03c7ba5f0144b991dbe0c10d5dbb6cb22a293e29c3eb967a616c3ba827909034442f4134c125a92f38928a9691513e425036be38a545e6ee19c6ae091a24da32ed88e578131e4b6fa787f98d628fc46536dd84bd13b25d624aaea0f64575d4c90b3f881d09d3e8d16f67383b0dc374310598e2b4e68add8eb1738d6620651c7fc9c9b9ab2b8eeae4f5913e495780d7771763ddd3e574ee2456c23d9ee4a97871d095a1ab84e39c5e87e90e8592524950d7c64223104ea4ff458c8780c712245ced0c9a225bf9886486c920040f172bebe25193b96bca78deb6fbb2e91ae5cfe1f0358caf1df8c531c4fe70c008002c663ea71908f6b01c3e8c1343c533c2637ccb2e31b6a639a9c7a13433225672d15ce48d953e8426213e18a5b8ca1b21c5f96f7c7c403e55fa754893a9d44b2ac3a4a881aa9fec129f2cc16ae302ce7caeec72004b3be6fb35d7e5af552e49002149bdabebd7355e05c5e9f66bed8255a0560fc4fc6bf5dfb32cb7d2e822429a0a43c6dbca222363d986c5004d4eedc9ecb9e8a72c65f318a22c62e3746a2357ea2d02f52bdca1137988b80b3e507599277a8abba4279ae1c54533ef95fff2fe78cada979b1015b1fb936844091125f5ef0486a81a80b4cdaa6f9aed9fee0d88f037da901c4b44bd6a1973248bf83024ea59e9c2f367bb51c86e9129ac3d93b5699a9b30465941525bef5ac5057888d988cd93734d8199305ff3390a508804340fcdf69191041f02cf3dd43060685f54c4112dd995d6bc8ad6b988cd44f449705a755ea6e87210c88df4dd03a592fbcc516c27318e32b8c108e1c26f0075eb2e31fa3af36923abf13951c20e15601f167dacb748b4bcecfdb0f7be1fe4af430eadad7e10c293ab621ba633bb216e5a74375686ad689a9c6a300f7863605ecda713dd1f633425ea0f1e5e33ecea7308969fa012c00c617ffd007faae06c50b0a1fdec08fdc05e2d45ced12691aaef971e12732f643a6e8a9e290d7167363ee3b30f2fb29ad03f2cace19b6981ec8682d326c875c71b4a14c225f1a98be3a609a7d37d52b0f5b9207f3794a8336c194ae465fb97d903cb07d2fffd2cae528f881aea04065c83d28d6f37c3cd2e79c9c38cb20cc283d2b1c264e7c9301bbad440bfcb801582320497c020d88dbf480fa08998abb78842711c169a2290813652e52183b411c7af822529225aa5a864229b7acc7dcf823a1f1c3f090807396cd74da7527c2b66d5c074d2f81fa95a9ae231d05d6b4bdaec2c72cc57a9cd8cae90863db11457db4f07903280f9f76a8fcc01299d830881f7ccd4cb5939f5b977e108fe89c8ac07d054f59722ff5d5b02f79c9a3aed4240e36bfd008937830e8798c89449c718451a6d452ab953a009cb7839f8e42214c91e8f7fc043a3da5e10f42607fcc26d1b92e60d1992b940112a9181dea2b46a6434cd7624318d313e6af0632030f6e67dbfaeaaf5fc48778e8c189fa879e257e17f3d444346f55dc2c8e53a6b269fb8c3723b542e59a7e218bc010790c9c2d240972a804383f24986695e95276045370c106bcf50cdea0c3f142bf63c0d7de2f345c2e88770e936c7c492c1e06232a4ee06842d393efbfbfb53e83edcc9cd09cbf45715f3183678b9cdfa0fde0767f3282225a7ebc079c34979970754fe31cb5d4bb276574692db260cb03c0033898eb285c9f80de7ee41b62dac2764a0890aac4b03344d524eee1e454ab10dddaa184a37be408c96675ab60e6132a0bd875e4a2cee04a8cd8150264da3cad1eade18fc844ac3d646938af3998aa1d931647c8a085c4d7eace76421f61c758770893d871b26443556b7cf27fb528ebd938883bfcbbd7b038d05bfc28f18905230f27630252714df369ae55b98d488fc792812ccbf8b4cec4b9f7d2311a27150e104bbc62139b08114e500117d56d72684e3e7ffca5a1adc903ff621a61295b30f4b99fba9c706aee725735c704b2fdef564ece9939f6ad3701abe3bc9c55642c58107c19bf7625071298c161d749af99bec60fabfdd60e90216c22e51e008ce0b0c2f158cf38660d7a9b470ce3d18851ed3fa2161b70b49ab047f1af704a9eb22fb10189116f4165d9df3ebf7ec3519c6224afe30fa423c95d1552432316a688f338c3da500891ce64179c24dc1f4dc8d9e8c8527985ba37be8121ebe161fcb9be4c4d33eead0871201d7054bd4fcbbeecc2a686da184c509fc4a3dee2fbb229b5f4d0bf4fbecad7812ca6786e6fc9cb069b794d0af441c31349e5e4e4808cf844e68dd7e9201e6ebc6351abae7bacbf147964cf0206d2e6f7684978bf1599a4ccc2a95667289810206f8ea7a798f1d63c4bc05acaed996d1dd13a7dc01db1fe1113f7fd6933f0be28bbc64d1623ecc75c251200b7d4b3f099f20079459e3c811199846c2e04334f1baf2b851d5b85397a417d3f912c5cbd79239e2a940fbe9187b121af2a271a0fd619fe11ca70da8875578c4cd5b4c43a1080785020d990116269a286a40b32e3c8ff23069f2458a7c98c840bd870bee9cfff318c058103d45af857b7013d86376c40285ce6ba9013630d1548c3df76de4041240db7f230a5476f49d20ac4874903aaec6615dc0eb9018409874521175c561583b3f09bdeac4225980e8a16cdf0a30b2b8a59efdc2acc81e388dd46ddc3a3f9ed18e7140e9b59209c364b131489fb2dd7d5876d4734d1e6c7606e012cb880a6b52fb8e74d388f4d19686e03a9c6a000683bb0e9d93d0cbd9dfd5ed4069740accac76145e4683bc4d0da24cff5d8e999645a7dea2460d7b334dae47a67980fae6127286cb6c62160c00d5c45655c002bc977c17e60e24d269091400a9a0286c8561dfc879fb890157f80e3828f8966481162b4b9522af29f45b4666c421bf60bcd10a1bf334cb1a6bcbd4cd3fe96ca9740302a1813a60568855ce0d49af63b038689d7d73dfa7b43449e0d23df684c10cd8d8f5195b8439f376519cbe039f1dc723506d1d95085a002f213d051d669ed403d4ddf215f44733fbb942c689f555796290c1eb1597b0e83d3c29c3b697e8c001e9c9f9dd280e6b0b266db9ff0fd02a2c1045fc97e60ac45bb2a048bda935b3ead285d3281c58db68540b8c7c09c406d090613bc65fddf1ef858e6850a457d65b3e62cf86119a5c88371b3ae529d55f09dae156735757b282083411e644c91cc8f7884521e8e0e53c7c751bb271a1427da22df2fe082d1a566f4387c199ae442a6f47ff489caed108dbd9c1946cf0d209e5e24865732666521d6c7ce41da45708ac98493260d93e6811c17d1d6240a9c363259f2c6875f03b23c22601b652ef61020065a2616eb05fc43e41bc44f3b62b4e1452902684cea9e7ee6a2c96449772af2888476dae4167f3790449db107f5a304afbbfc51847c0e93155344da5b07bccdacd91dc64f8d3c15e1644124bf709b24db72508bd107668f708a49c784047b20bb7ac09dca89d228b38562303bf91892de1374d08c1afe6fb6e450ac789767cc706b81f7ccecbc92f13a7a2d928eb4f27d36ae9b82a60958fe6e3d6c88c82a4ec9b8f48b848d7591b2a3c8e70a711d982488c66ed4314d64a94be3130a957b7ee3dac8b64f6f31374e2fe80cb46475f970ef7fa1f94247e285f8e278c2a9e786b5c8a8560087935d8fda225e2f49a6fe3e4dc3081050e5b8ffad814efd2a4df54617f9010d254ab37eab8e8960ba4ea49b9c3031a32899ac86e1e5d6da55fad3a2f0f4fdcd8bbc2f349609ec82f4ea875d0476a7288e83239d9cc961695a7008dcad5a92d09b03e3e358d31bfd355b58dcbb99376050ee169b87494063280412e5a458619804b9a04aee3eca07fe1c93a70555f594f34f4ecd01d6a2f0015e7ad80752d17709105e7a4972ce1c1f269c57beb271e3f1ce22467e9ec31e93981b993b6b649338f62b993551a8ba833be7598e77d137aeebca1c54d7b50cedf260312d56111194405d5aad7e4cdf08c5603c054e5235491364aa6b8d8eb02013339649784fba687faf7ddc84f5e47acc1b34930ca38a964d9b23fb8787268b1b48e8eba33dd9b236641091c695e9ba0597856c33a2f652eb9db010e00f6872d1d05ae86b2aabd7c520f7beffae843ff58f50daef662b047dd629fb3a3d1a48703c6f1555fb2d8c49dcc0eb452a65e5e131d4da778895af986cd27ed46ed2e90f448378dcc0827c533b7b14e3393301253dc1be176323820fde6e54d96e72c8779fdd15c2765aa8d2cd635bf21e87083e9ee76b82131f5f3e03cd500b397c240d5e363c3a5afbc7cbb27a8c0bfc23b63124f825839cc48bb54368023dc316689ef78359e6c086dbb4330acce28f30192aa66e30e16650f8aab08a319f3080afb0e4bb580439270e434438a804305255ae6f3b8981b3b8c2ab57aeac693dc63b42bc83ae6dc639f16112a4c7d9deaac6adc913ac5d4a591fb43f32112df83cf0fe849c8ff65f7afdf6fe697d65088071ddcc922d6633cc28d0c363a7ea6c8305ed6991885c8509207190234448baa270705a5a2b0e3581648f7346a5c6988b42dd59568116b13d3ad3244da4275a72444d4c8e96e29c4ea0ce926b510513470cbfc662530d58dba42402af5e033690885dedb270e4a5f501ec447c40a8a6f1dd025a2900d076947dd3a6c78e2a3f704411277e2787e1e038f3a7f4a267f64ca55bcd040274f5ffeed125e011413feec35a196a1eb0af94261aa50ba4db8381e1bca8be9f439c6c189ece18973f77070bf2a9368de35f45f971efefabd2d961dd4b601be969cf881e9484cf806e741f3da7cf3840ac56b1f2911b6954bb92aaa7c52eb1116145f62e09d8f78b7bf4b04eb2bf1e1364b3059feb0120fbc55cf49fe2921edce2a102afa12d90ac5198c36fba21afb10f6809f38ba0942b244c196620b4ce9ad685b2349f012b093edfda4e60fb07f899cd94ed0c939877c142299faa7bb2e0124e4b47f0aae31edccac30b99babf1dac961299e1e3bd71454d3f104c6d45ec03e682984e0cef92bb25d633d15e57282cf6124ad4b656a4ddcf4ca3f2a51880d38454678f213c4c8c4a6c45d51bc205a965a8923a8081a5aa97a2217c0ddae4b83b40b28b930a7abbfb70ded81f125d61009e3e76555fb0fe448de680d19ce8fb0a017a78d25908a76c7cef45412041be44113c1a746a7c28f680f91bbca13584b830c6f1873ea19e468980d7243f0f582be93404d13c9f0eccc89be85d29a870a24078442badafcbba90909530391b4107f271556148de34bdd516dc559b97a9e725160eb2ac993d8f76d53c5fd4788c3ccf6ccebef253f7f1fe2a4bc6ffeab5d26a6d38c0f48109240caf483325093f56cd9b060edf43eb437261b1648a62a036f275c88e80bd56bdd4ebb18f14e96e4eff9501e15364d9a0cb7635d92ad4939ad41ccc3a34ae226a39259df5f742139ebc52f8bbfe903023c6bd7a6c29fff280376ded9dfaf44d47e65a17384a7bca9022a5452abbf10778a5f762b032b280f6aebf4e733d5df9ed223d45f70c79df0d11bb1c6e4a4f9bf33858b600122faecac8590de847a1da2ec60d057800592d0a85de528a30173ba3cde45945216599ee498139dc38e7777a75c981d6bf665205eb5595d0ff8dcd999658c1c38bef7f5be028ad085382878f9b7ae086616111c16c99476848a68c0b088f72af407ae773bf9f8f16ed78eb631eaee87e60617aebaf88ff75ce82db1429b2f4f6836e34d01a1df5ab7b8b652692da1a59113710674e82f3b8b1f1af734bf6a84b82f48520ddceac576bfebb346476fbd69f9831bc8f6fe8cbd820fce048888f5efb4f4ab399ca442022c722bccec2e0b34595f8c554347581414481397e0e565f7daf4ee68741f433d6ce30bc898a601c63c8ae63c319e457278f6e6c15ecaf3695798da5da65ea95f38e2a5d6be9d8276d83681ffbb7a288828aa4c7b313ffb5f12b385cb8fbae2ddbfab2de7c2ce3de015860268cbc1fe3e4da6e7d37ab17cf7f94044c56b70b89ff789ff8049ca883f2aedb9621f24ce74990aaf32b0bc4b46264c06042805f7f86f86d06346626522422db47d268fa840c22e412810dfb8831e3180342711d5671138c800b76848ca8db1cb4b00f3a6932c058b63690a1a9bd2f4a753536a1b19de2827e47e589a827bf2033fb886755f1a0422a5977ed686cb7589c83f39448603fff6dee9f8a5d077161732f2660a5da9dd3a4d71ed551cdecc8838251e455abf493c68b89270c66028860080c0436c6f345e7bdf33dca094c244ee885ac2d6d5c31de5aa4e1f947600be7b35d4a2f137afd088fa84411aef2df49d4f9bdd94f7f37757eb6612209afe62c802678f0dc52b122a30bc744d8df46918e535559af75bf63f47e2c8a7668c8ec66b00fcae92114e3357304ab18a13151579da7fb6b55dddde8faf9fb4799516a99e10c7578a5c30b6e88f8af80271c619b4bd994ae2918bac92fe8e373201d620b8159801aedf83a81eb858db35506ec9cd8f2b586ec7bec255ac752bf5f2a7e1cb959f7163bb7f95dde2a944f0ad3bdcf881c18b5c790e1086f05d678f0e18582e1dd3d0d6d7f886e13b77c01a30fa234453dd6ed6f388899d9dda905fc78122343b6d3c35c63a0af111fd97c2d43ddf0a5d161b719a9b4a6115f5f526e99e16e8c3e8de68212a45c01a5bfc236cc808b2dce2b03cff99e55874cfb7fe308ee18c5354d74add21ebea2e0d16726560ce8cdaf1558aef5aaf7ced629d4733cec21459e18a91ee6fdf24d6913d08d888f2c559661d59cc365922bacb20c424d34d64d9e6a3e45eb5718788bb40cab4ee91a2c691436db5b1b97228f12333b42b1b117861d663b5a3fbd33b970502938b8d05a462332aff6cd16e5a00607d66e0085b639bb42efec6a80f199f69bf70862a50a886c744292d44f0a10c3a680b8cd8b7c4eeec11b1adfada0ea61d0ae1455dbd991278ed6158f649b0e08ddc7c5953b2df620d6c8bff58673448986ca4b0447df4f4b8fd523d000414ec5f3a74d9438773dcc5508695859c5ba8a118defe46142d8529ee471cdf65b64c31e73b232ebcd48f024ff73c5581266ccd47c490ac8fb59ea36089186d6b8889309e7ca31dbf8c9828bcb9bb93dd768737378fb55af07ada1b7892cc2d03ce6e4a9ee35ab91bb5aa5499863cf59ea031e41a0eee168b471f25d2fa5e5c0dba0fbaac0173864c3794630ccefc5731671e6c022e66a40c206ed5b55b31aca0b5963fd3348b586798e5bd5ca944b4b76fb6c54be8dd405777c6d94a6d2e35d195c02315420e7b0354c1256a6cfed21a2e7261838f7b4646217b5cd8c861a5dcce76e4493eb144647ded2ddf821f0e0fd040ee13e3ee7d2678b5d43ef7b7c452988883ae8f7fed70bcf00999d0b12140cda6ef61077ffefdb7bb012c6952cb491f10c7ae6fbfdb473f11d3db2c8cf970f52335f8dcd84472deb50e4defe2fde6e7ff96ce6b4f71c8ec783063a52627ee36489e69f6593361b01e0390f942bb0e7add0b26552e3355074b5b7160518ab66f21c19e22c54577da7b92f6609ba16a16bfd24cd1a4b5da436d9ea13d59681fe39c8d44c8b7a3656db70c87c3c2be877a605e7df5ec61bed3640f7ddcb976c5001ef0f224d1f58321bd264544cb140ea40b04587cbaaa7c2259fc4766bb0a66c8a9745300980c6e4914b9c9c9592b8b11451cf532c166c0176c766c99a8c53caab98f77a7842317c9106d2d111a00c7580bcc04c996e3030c00955a25fc1323edfab91d84bb1210553c9c232e9216c8579188d6923b67322c2a4351008380d295a2fdc685795346725d8d765b49f1a2b8abd53e015e7009e3d76a15e1486d7000df65dc9272b037df73280c5db27e276ca9b289c83359d9f9f91662bd84143c0abb620f2afb719131196e526abdb156716cd6e4094226e2719b477a53fe1169f4cb5521c52720d577aa8e7dcd2d7cf99d7f07d5c16cb9e157caf6357e09b719beaea0e7ad525a9ed0e040e0471bdbddc06f603417e9a7c9b3ba89d29152e439b16801e7f52d7e4700c08866ab9b109014955cff16e8d68ecc8cef0a53281cabec7d283129ef20e4c747ebacc7cb7a77a329e292ca3b848585848fef40fdfcfc68972f39021d4dc01d5475a374f0335be19ef7214e1059695f4e1b0f2f3926074dac489e5c4e7c7427ce845dde587a2dcecf6602bb4ba72df2aa5b50f1e3fa01b685e946327e8b98b04184d8deab0681208c7d85bf708ced936505a5fd4de5b944333e1ebba600a6649f44265e43187f8808921bcd3202c334389cf1603fac1f87bde986c0bb7e37c00bca201eb91cba46e5fda23fe0a209b7ae61ae104f8b3ab1604cc4fade60bbd3a0e83f5037b45eeb6155dcf560b0b96f7187740c06f89c1ada0cc70970e8917653f8bee595bfe06382ab227c4f58e71ca4618459c9a373b65595424d3a816d34f73b5bba7d7c2d336d276e111763e3fa526b1abfbb2faec4984f1d3b0cb78e41af1b818da8454170c3340ec336abf52fa19559c9f3ba9a43c2d825b1036f39316a4802eeef27d54a6baeeebe4726a57052e8e943558656a9cbb1e7d16409cdbec4102d31ec9e2a6b6387efac601e34ecac989913809c2a634d1837af2f092e04c8d158b3606b54752edd9e3b41626ea541d17bfa1eeaa59454f24b3e95594ddf65691081028539207764a40143df5c3abe573ee2865b7a41795113003ec31d531ded76fd67e826f9e35d3fa3b29af1e276a68f2f0dbe5c02ee48b7b8efb603e0a7f626060af541d7f501679c5d018c9d1b4b23da885d8e47816e98f83b3fd306f709f3db5d7b96077f5ac21ecbc4d41ee55a87a19e7db1d0d15fd4ee4adce9963c3e70ff910e4a7ae0e8d6c88bc2d391ea836cfc00aa70beb36525deef65f3bedba7b996b74f1da1c156f119322eebb4043006efec1b1a918aaf27089e61179cee574574c311bfa4f5c54c26ce187ada0988cb18aa6f1bf91c94f02b8e65ff455742b950885c7e2f53ba110f051926f85e04a978b205506528a1d21ab444045aef03c8592092b292742ca671ba88b7d66d18cfcefbd39eb16ae95d9595be995e4b7fff77c7fdfddfc1131a3643eea235442994dc54410520c4164d18fc7be17210f1eef3f74f3fad8b7493d6e85891ad6d23ad114208d964efbde5de01160d6a0c230cd8df8a4a29fa654abf2c32fd2ae2e458fa48abf0b673b7c77e1f667f8743eddaa78aef92a8e2bbff8061d4be9369da3b1ce2308d4c167d2843af28a8c9f7c8ac041581c88cfb7234d54e9c2cd54e349a737eb7e93cd45d7db061f7249637897df697335966d84c9c8955b231f7f923dd35ec837dacecf43b9d897d15835cec71227de1b55659955d7b04b10cab6ad87efa11d335eca3ddf413f6b1b2a3dd845519761fd337acca708fcc118c2076f9c53bf615761bec39d8753013d6705cf56a0d6f621f2bf63ef3dc1bfbf4bbcbb7565865f14ebfc32a3b3f95c5aa1ab8cfef74ef739faa1ae6b9eff4bbee4b4d1676ee8b9355bb6040bdfa6c260b7bf6ad7a855dfb30217a15b4ca1850c61e6b30cc2385ec6d264e8331469c2befc9ae1ec18bb973ccb7752f56d190629d6b93a3a2f73bd9bd86c3636fb2ec43eb4520f4a80884be5974f6a0851c5f466064981c5f51fcc028c70fb24c31821717ae289430d262f49b7748e5f9716a78d3ebd348a36c431188a0051a4d0c4901827544185ce21002106578f922e7200a920ac628c2082a201125c890a28b163368020ab085c0230ca3276a8006192678033422c41157bc0c6189288040e9451ec11a694041831ff8e08b27a0068010841b6b9880042d5842fe3c6184114926e0620955dc00bc000d6c30841a50cad8e28d1bd945eca20233c608dad2841857c0b6c096232cc1842c5e00d404d85c64229660043184b0441366800d84082ee092a4c6101426e416b90d2db8604328ca9313c001760d295842162788a0850a1b60cb2db98de8115bec47d52a0291cf968440024d649cc008440114475b93600c2530c4bae0200964125f3298c11721360490494c0dc089effc2216335c5005dc2a37bfa8450b78a00402a18c2790c00de3e6274df0839011d89fcd1570bb588b2d8a2802fba381029a9184173f67341e62f480470f9890d27ee0427e55c4162b166cb1a61500a32cbc1af419a3496bf801679226ab2593349354c30f38a33459712649ce57234040a767ef543b13d47e353cd35ddcba5e13af158089a33af22c054b60865546543bafc1c208cc700c40958913c1d4fdc16bcd24190d4d1c1f411327366ea5e467b44c1ceff233471307e6f233481367bbfc8c6be24c507e26684668e24470c695e56592647ec0d695499a600d1646a096ddfbba9b2688f226d801653fa98c7057d910414e754482db25b861b1a228cf28cd24f54dc462c550969fe132b33404f54d763ba69f64d7fdd3673a793fed989ee150bbc974edb39265dfb42fb4d2b762a4c3a10473e4b8f659e9a3700873ed3bdd73e010f5ee1bcc37a334b33459f2a66fc6cb64c97b5fca3559d204f3a1be79fa22987d11ec660c3242b931f78556b6670fb99fbe659f6a9e3ed5c4d93becd33dfb09fb8456763a7a53ec62a0a2618a7d0481928aa6c910c9204d1caf6794264bbe9b49ea95bc4c912a004301180a4095001005a0289b499a499a519a59929ff122cf833bc29311caad785497e0b56692fa05de73eb4959fe0ef8a284862be80738a2442fa44411832b4e30c69012281f25144fa703b0fddc3e0de11283479313cc302135042c3789060737c89de5182959d4e505e96a2f5e4f1091a91255bccc2a22a71c7fca5442d05f423315d7bc676462d51114be807d090671609f9874cb2327ce78bd9898f262e28aebc584d052521b1efdaca90640b269dd1c3f310f84bce4beec0f60999a717f765cb87c356f939b5c82e8d7887974885708e5897738917885508e38ca16e49c5f640b546cb588993471640751907e7e3b1e483fe7a4424c9618b1892d4d78c9ad9b9bc8d2c411b8d0249ef822c627a808c2174b1c392183dcbad909a01c7f737c3931841351724c129ad2d3cb9fcc75d4699c383b95fd071474c1fb8c498eaf5ff63dc90131a0273080d88328783f3b95c546b0c7bce381b78b5cafc42657dc73ca54ee5b3f797ebaf2c53e8b65b25c148b570eaafcb584117d2d51d4b2718ffc22230d36c9061d224fb94a69a414cb2c53ac22d2a2997299621db89cd281cb353fd99b2b906d784f70b56fea9053a633ab5443b03c2f73671591ce1377042267ce376f3d6977b794b2bb63777b268ac4bded5d9a948b7e32de1b090717573f705822a86609d711652871854aeea6bc37654c72b95c4168e479ea9520cfc7dc608c3c4f335f444ce489145f4a8c8086e6eea68d1278fb0aef95518aa1200e28344bdfd07c299dd2098f07b91fd32f1b0c91db8a097237e7e140fe10242f9d739584112847b5ccb6f6bb3639945a7bf2ea6fc4e91aeb65c5b77f2893ee36436701bd92538a00851b43502103252011840435a4b862892625c832840bea10439a4e4a051643e2624b504cca3d628b31015b34001088000417003d31a58aba82388c9090c0650b14f60a4f9e46d4eb89245e4f440124b164f29a74f9a08473e5f936ea2e94f652df348e4d6a2d2ddff22ddff22ddff29dc61b4da37cc5af2c1fb344527f8f587c272bceaf1595b28cde125b9459c61738578e3f70e89b56aab3a59c7918e617302f43e344a37923f33b48fa26be908822cf6380762a668a3cbfa2f53d12ab88d0ffe439ffc30518e617307f7b1ab96aa6508c31c6186394b88b40ba64238a86783dc125c7a228454007da3aaff050952e2dfb467533f7556d818704871cea93a8acfc8c70c0407d0e8d0300b03e49fd92c68947f5613de57a099444aeb5b65ce52a45aedf56130787c9aa48d54b45a4d75a8f903411b57e556f539f53af53c1f024f6ec18f6b9c79e611fed59760d5fd50ef6fe307c27ab1babc1a6e44b6ec696b2cd423148d53285a69419a5936e548f706dc28befa489736f27a53a69f5d529befa922df437aa63a80644134b83e6287d73ca3700c8fd39949471a851dfc4570d9072bf66e91b493b243a93851dc9992cacb7d92169249b7bb13bd24793d55fa976b4d491d56461473e59d8ef2b568d1a2787966a45dff4eb51e30000ec57a42bb95fc16836c07ee5d237f1550589dcaf4a358da5eaa5a79d8f37bbf603ca0cdf552b65d32b2cd52bec56edb322ed0fb829152125cddfc9aa92d392415d8da82b4868e2fc809256a9456d54916a5255c2b460476d84256148554b35f266359ad5c8a82e19f5aa8572cfa19aa557461148bf5ef1e467ab1ae53eb68442020039beaae080ca080eb023a68d01a8a0f56d6200b0b2d9bc4489a93c1aacbb6f0d95a0dc8a4631507982a5ba3d2a401e15277d9bec8838726b75c406d6d8105ae44681ddd8a5930b94d44173313455e4568d91edc151aa870655a420430b31ac2803eca035a65c31c55015284e80bd832c5815d8b5980ed868274c482401832684a004294643007bca192888f28415407091040eb04e620649180d6db1a665b35dfb867f84d0509665d97ff2bdd9bd73894d75249467102d91f90615a604869812b83dbbccb1a66573535fb15e4610e5558eaf2943f975842bc7971146b9e5bd8c38caa878034e912267e20b316949be004e2142b610734b47116cd20dab6288f9fe27c7af7573c4b0d71429761e9b723a41a3ec4939131376abd24411af26544029ad59e6e5d54490b516d3b42b60bcaea471efcdb62d4bdd22b9d79525bad7152a39c6c4c9ef75650527aeeb3ad3e974c565737c5d11f25e4cac81e2727c3161060c8742a160b80803c3c41626b8508d8dcd9b928b31c63805ed9b08c56321c72c6244c9b15e0cc8bb9c6c42ca7e41125cb688d94a678b52540c924571bda28f315956f07ac1952a625240848a2280c46041166994e08948842648b8d6d0d2c69333ba0a0c8cb4c0872b8c10842014a0e007104a526e00051a558e9c78a38260285a1ba80165896bf5d2dbd033b24c575aa5f7d2148da1a92e99faaadea63ea7d62fa071aecbe5720151299d73ce399bd65a677d6ad65c6badb5d61b11a325954da4851b34f801ca0b869630d4c40f146f601145880a26aa28a2911882618b121738b072041c4c818512cc38021329b8e275c69530b010782deee68e41b5ddd5f37254a1821ea4808dd7118c702399c8f4296a413a440a315420e50625d0a208a438e6c876f7ff821ae47e4dce737cbd60042f685284cd8d6e94d18411ac208c3674b0455c830b127ca00110406148b1c416b78606c16bd5502a6d88a04fd164fad58c96ae4611411368e00408405cf800c4029c0c95218490124b582881a5768a52155c2a43bc9c479fa2ff0f0b9456a0440f809074032c407a9b898373c611161d10c9a0cb0e404a140596298d1307031db9442e17033950239ef7644ec95ddab469379d3d1b06793bc9ccf5ca4a69b15bb3df2a8f555ba5905a8f559da514fbc9c5d8b7ace52c8d89f1628e99b1668aaf4bc62857ca5d34bc16955cfdb8cae9b48c119531ca4f6cb1669352dee730711a44c00a5625b87c928ac363c384841caf01896392acbe8faf8941b02954c8f19ec49143de9b0b99fa90944352e6ec5812c429b2fd6b07b93ebe643787a4919090a6c8548a5ce314b97ea398a5f4bb7165af7d5c5cd96f3a7de3c5554c161d3c3ae627a754c1724867e2783bd89f2df656e756dc622df065facdce963556d3959adca04859a3bc94974970dae34d7d4480f618e5a5c8fbeb79292d292da91f4c86c691571c19d9914782833c061ac7d3413ec9122959ba302bbb8865142965b284a44b3ef58a578f612ffea06fd1a25e9aae5e9a38d9ed2750e3d8379a656f336c95ee8f1f35354d9369ac9d47d9de7a11b24a6da4d2a2d1512375522b655fb8ddc787ec1bdec2ec5b0d11cc700f6117c09e16b5500bb5941eea2a4dd44549494a4b5edad541444445465a8e90b8265ecb2a65fb162dcaf6d22583a49094228764951844c6b8b2925d9a383fa07df6fd80d68b3de6c282260e05ed31a1184466dacf6c148a9032eb2ccbb22ccbb26ddbb62894d8826d7d7ab7b07d7abdd457aadcaf4598baa0255802d2143279d19cdc6cb26ff3fbe64d9f5e2ff5154d0eb6c9db2a6f9f77d3302797202fc6b0c5b1628a5babdcdd3994db648e9994a3e8157d16a35ed1268ab1d6cfeea25ed1bed22b3a3f9b4420f4f39b3ec8166813af35898866d1349a5ae6d144eaa33e6aa424a5252fb509af3fa72b02a19f6f4dd7d292977675500bb514232d4748494a52488a1c9255888a7a08871c9280412d9b4c6d7274643082846be251fad6bcde2a0619aad244b38bda68b6961844cea4ab844d09768f611fecf71af68b55f1d68b738c60ef86c4203312ec27b30c13a7414b29ae4a783463d866b262d26cc166bff170f39cdce35bf2058973640bf6f65a0cd2384804621f9f5d9c63c4ddbb799e964a65b7d8e224992c9b86572b9137313581574f53bdaa41e6cc37bc56ce0b5a833e5edfd5827ec0165ee496cd023c1b840c0417bccb831e52e8e0654e48c0b34409f06e1519bc3bb4da820504388102b618c1095b00f1f0ee1b3978d81a1cf0608890e34504b240e2693ef8a4608077737c6d7902cbf1b585684b952d3ed82f52322173c24b19399a702199aeb8ace09c08d2c1e94ad1121b13432e9869c2e8158412744b04c13871a445139658ba9ed4a20c1dd2051b9b9c25b4342b5ea90592c963e2a859f16a71f4d2a24a1c589c5957200a2b05f05439bea240812a8a2630789e122ee09dc8d080c7e5f882820b1bbc1c39bea0c0027aa61c5f503401c48bc9f105450c6cb418ea2cc0d04109b2404a6243da929774ceb1e9983326070caadb9bf334e79c73ce394db935e79c1db775b796cabafb6273ce39bb71c4b66b77d3796f2a556bcda194b6f46ed411930306e59d4c1db769998c294620f13f84b035b5a89bc94a0f827a985ca3fc66965ab7d159cf71f25342f3929a6fafe4a722825b9e0b2b55fc4f8e8fd1b2ade34c374e968a887c7f4e9de9420442d3f3fbf2e69d3a21aa483f19b1918a63de894620c5291db966177aca38abe9cbdd4cfa677696c6764d89af2290ac6fada9d4bdf10d406146bbcd39a5774a4d56bcfd72229048692bc6ce14162bed960bd96b25e81a9e7cd4887a528bdd8cfbc43975d2fb641015e9da382ecf2473db30aeab3451bfb62a35db6e842131128fb2adab18699d8466b4655bed2a465a173591d64618c3b239aee3bc1844cb9da5b364a145f0de5d13e7c52059d6d2422dd447f49418e3acd9a6b5d014aca360536a0b4dc186a460536addb4ac85a6604352b02926ced409d96ee24e5ccb855c02eef1395e74c4175a888879b9f20d19198c99bcb410b783da755b8893c75ded743a79494a6c60495d7472727292ba70e9c225755952ead269a62ca9cb92529793a79db8532784fe6442710b681ca965b2fa56e4782d5b60d0391d0c9bf802c41724182ec420264934591b2abe20b378ab9fd6ef621f074617634507860e9e9ce9c8d937d9651533512d128c4e22c9a34e82618153f6cd33e1d861ad13927ddb348e28cb15198ddb88b2181565c99d61140a05c3b5910d72bfc5423e7915474af90f1b1758f10599c4e3bdcf6b1bdcaf669d94d237a8b8d241844c2b774474701d912fd7d6d256749d45cac441455612df0e392493c84de493190ce7c520a6ec62da668a7de46304477859f86056ecbdaa69d664b57ee4f945ab1aab6eb8f41af6b9a7f7de53ac6a55cff9791ce673984f32af95d795259948e8e79cf394da7401e6ef97335973568a53b30caf25916c3a044ab18002233d9963822c0f838e0c46fa86e686ce1c74381242965b96524a296d64163ac85293974592465e224930683c2a42ce4eb33fbb58400cc38e58908af400b9628194eb1360ad150b177c00f4c88a23b8b0e20d24206ce1e20a24f29cc11540c83054c1459eb30bd8dd3229df202e0aae644a4f6f77a54859450b54991ebb4273520144032aace43432bd4d9580522d320840a62f2d4799fe00fc410b324523d3d74e087d55c1ab08999ebe407ace8ba179cdea47dffcc814cb19993ec9f43930e82c60062359dac8f43b3c00e92980854aa6f43a2891e997f44d97e90bcb0e327dd8373f5c3c8802a86f7a042d659a458d4c2fa334bd24e2c5032f32ddf2ea81954ce3174441395291e9cb075a647ad44313410873a0f79a41ee4faf5320a54920c4ddeb01600a3872f7f5b61adcfbf4b1cfcfc7ca4e1fc3a9c9a2f7b98f93d543929ab71be6b11fc9b06f98116dda0cabac668304b3ebb436b30ddbb06cb6765745f05a291b220804a43dbb3dd5322f2dc5b3f9119528d8d376b141e007a48f35cff20b62ca9225802c5b06f05aff4a3e4747062313673bf7ad264905cae7c8c320bf007919669047323b0a49295e46c0352b72114813bc7823da55aa23b30b98c5898aaab9e3254e2366ac7ed8e4c0d0385f401aa82c3f0318b3630f378c2c7b2699f6f51e03fa713d2082f43addc3b79a2c79eccbf964d03e2b5dbd922b29b39fd997898d52492556dd80bd56ec15c32a23f6530d50b533d500b157ac928fe0c52a499fc41ebbec6c90e0bdcb4a8cdeebf4e6ca2106893528ababaad305a0c73e19e6876455f16ab2e8a792d78a4a3ae49024764f4d9cbe6c9a842dc6df782f3e151ff3166b368de6abc9aa666c41b0acbd3eceed9bbf3fd2fd9bdd54aed8f6ee4736ee1d877d38ac56f92555fffa55bd8d86579355536b78f7f34eb2f755462656619f8d5518a64064d909511999efaf83eee777ee67387f8fe19dec19bd0abb9f0a5bf2c9aac7be96e2853da46217571542dca80e497a5092ae1c62902dcf63ef0153c26ee2aefdc876d3760dabe88655b6fbf347b6b9dd673e7a39704926cee947ba73dd4f1d7e771356d9d42ad5610ef79024077a9ff9de2e77b8e31d0d639bc9c2ae7da3365992a82acec10a2dbdc29e85a3f2850eca71be7fc3c4aafbf9ed8d55d90d1a565dacbadf99dfbed3d7ded7ae23c3646118f61c92f440e50b588eb1535d5cc3fcf61be637ec37f4358c64b2b0ebf4d13dda624dab85224eabcbd10899d688333b8b75268bdef60082202c9a68492199383ab375aaab7a6c95e9500442631059f4f494e9cd928e66cca7b2885efb7a982c1a945d6c04fa6b54c4a3bae1c9a7ee27ce9cdd7d9fecf311a436c9763f95922232775003750165919daf49229545f4b78725b2e87ab32f0eb658336fbf1dfb114d3b867dacecd86bf7c1be3d82d108865d75647601ad9c487a452f64a75742a6ec19223312690448d228ed2ff6a9e65515ab6ac09efd88fdc53ed8de273b483f6fb2e8ed67f35dfb45b0a5783ae01c7a45b9c425ea02bb7913877ad49e7ef3cb99ac55b638586bbf8a417a3ee565c95abb5ac3435de6dd1d7527309741615595c1aa5a1f4e9dc6be982b4c6d4a851727eb399365adf5b152ef03f3ee3e32476119e6dd7760b00a3bcce9bb57bc2383551856d50083613e0f83e344fda23e1586194163904c0b6164bee19dee216ae6da273396b5af8dc0a03421303038b4a23da4efaee1701ee63a8dc97c36aa8b57587b40683015f51f70877b77d477baa37068257be8c11c75140e4f87f90fd840645b966d2a0d1be96e43043bec6325bb0f7d07837de651aa1ae861be430f8355da611ec19d7995865535cc1ce647660e837dacecc05ce63e393ef323328f790cf681b9cc23783f0d88bca98ce4c871d591d9058439cc35254564eea157f64296f44a9bb9cc751ae653ddc73c82393ed5c5aa1a725cc78fc03c06fbc41ce63ab08f959d98c3dc47c7733c82323130333a7280325f04757c11cccff0ce3cea3a6d6d26cbfef4e94c9611d367bfeaacb5aa1ae8bbefd07758751fc13877e65517db473035592b7b1bfb1c7b1dfbd81901a296fbb17d8a62e3c1dd06072123b934d4472ea2992464449786ea918bc8260919614b43f7c84594250919694b43db918b884b1232ea96864c472ea253929091b734843a7211c1240919e5581a8a397211e9489211f2f242baf242ca2273f7cd8134b22207298cb20483522acfd1251d58a1832c5210e5bca4a892e55de85446b6db10c16d6b19c9f3f4dbbe4f96dd81961ccfc8f1a50330a6098c649b184cb309de71c8617296f2a84ebcf8786eb3b8e57531f495bcfa984a5dde26474706ce7ed7c166dbb0db1b865676380e87dce3643159caeced75664f489463042bd83d33eb60931bdfb86475b02969179188f34519629054ceb4d31b3ff0a2cc9d29bee33bbe7f8a8fb16d6051df78e24f119ba87c77938a7d648aa50a368aadd4925bca6e866e4eca198af4ef8d50944d9798677b77ed6bf54fb8a5fdf4ed743a994ea779e64d72261a3c573ed1205df9f4f3703f71b862bf91a24c98ebf3dc6b6fddc7ae95ddd4cdf09666327531d4fb38bb22da4d11c8fc290299ae4c6d168fba9ae4d337d34fdebd7353c9129b39259c777a67d3bdaf07857c3af7f524e074c231fe3051aebf9676ee527643221843703e5d7644640c9ae413eeee6b71dfb8ef3cf2315f0c7bbdb218e78a6364f64c120b227677636facb1969f1289b908047b0c31371a160ddac52042b818047bacc1c6d8fa14b7b81de21522c094c21d2297dc3e49b4d6e5d141cb1ade41cbfd9fbcd5eed7dafdfbfe5ab640bf51d9fd222d9ddcdfb2fa2ac395639c2237a6b49ec63e62abaf9708dda86d9d292d0f32db8a2de6b19f4b32fdc6256f9fb9f37619af10ca1ba69437dcb2857a9bab51b65075e0a4e5b138126c9bacd94b5b85279fea55c49e7df7b2ebb917477a31d72b29c646a412a9aff562d7e97b7beb9d3d31cf3cbddffc3eb1cb6ff6c52decf2f393b36dc7f0daeb0f3b86f12082439e3fee75fabe859d67ce244d030ef9de39afd3f513dfebcc63f6d423e98f95f58ccd00db7622626fbbd85de39e5d8bf952bdda789099bbfd626c6a92f81148e4308fbdf4ae7142a24579bdd26e93b9cb4e88fc5a32f3d854cc1681c41c9f7d5eaf6ec624a6bd8a3f71afa7397642ac166fbe53567e97da330db768d666b738dcd2de3d5c9e72be67e6793b8637dfb37b680bc32c642019fb687a15e56d109b14421f69a0dfb6dbbdd23e9fd2b60db7e4273fb9cb2ef62ae3be25b3a39ca6d58fd9ab16f629257613b0a40ae97ba345ad6afe5ed978c975af59d1fcb03df0624465691464646478a8687aec9091c19e78f23dd4e58a57b42b26d5220b06c625ca17a492942f50d70b51b610e190ac298f5b4c72af915b3f9952dc6acc437b1e8a83b93978a0272c4374c93cc464f979aec559383c79ac13d2238958578e606429a5bc534ada52da9549feb68cad3ed6d47a1fb95533104f631997b7d8c664aa43116a9017620aaf25852842a60fe3bdc9a23f28e5837280f1404f1ac41e8480200480a1951df9503eb4b283dde25007875676ec250e8fa94dbe107de3a39b16b14599222c659a1c5f45d0226fa758637c6b56dc33b1acb5c52463b6628be7a572bfed10af106a6bf10e914bdde100404699622a3e4c708c3de4796f68a18b17954bc5d5830446441972b50f93c805cc1edf32a93074032d2e9a2472c9545c2d74f172d90300195d57bc4248f321720133ec8a5cc0782a2e992128f183192e1718b10f93b792eca0c618c2117100b95c606c408edab606647821434a8e305d62a4f8f6b00d919bbde2cd951783ad67ec18f09329fd9152c83644e6fc23650a50c8aa2a52e9abacb4ca8a7948506dadd65a6babadf2f5e391983b820717985c669ce127f344dcc3244fa3795f6f3f9e7bfbf9a98a4c9efbf998e7e5b7b5bd4edb2ab12fa9d8a57dd74b21d2a875df98878acd5fcc5a4ae71a5bec1b2ae7a72a2279eafb53d2d323db2fa9b91f63af785cdff460f962a76f5559310f6d148cd125c7d7185572eb94e959c8f13546517decd5fcedf9c9cce35155f1e5e18ef07a98e496344b9494ebcf4f9e794bc516ad91b79dc800ca3d4f56891e26991e035bec2de64837ae6f5445e6972f799e6749e70df0a0e579d924f7c853fab0c5a6dfa8941ad89afbb6a4a5d5ca66bec240939494e7635a97a78f63a362af62af38daa7b84ad937548c2d1df7c52686962a99364ecce2dade37d6c4ddc4d9739cc9647ad7d16b2653f78c93f49ca53c14f320bb22f4316fb31b626ffaf6a988b46c0cdc6304224d9f4ad3de9d7634d05fa7c528da708be66d360f379b6ec2d474eee3e1666e7b8c4138dce2ae43903c3713ee99d95e88fdb4318bcb87bc9d620cf24b6b7bfd9169f2d61581d4635fab2f3359e3d83e719c9ceebddf09762732b74efa4e76dce464de0914f994931d3884f9e47127287c73e2e1d09b7732f31d77228343999f4edf81fae9bbcc43191ca26abee3a1ccb99fee64e2f0de6313871743910fef4f38ea9d93ee6f3b70888fa5701f6fec3b1ef677e0b0d67b38d4ee3ddcf1ed4ee879e03026e630389cffb687df371ccef970bbea393cef1c0e6bbd86c389c31ed7eecd7b0fb97b38ac391ed6b3aec243ef2ae03026e63a70a8e318f613701873c275603aaec243ec2ae0f0846c9ef030abc15056397098e33a745c9543870efc1434ed27f4d0b44dcb3e1d3b6450d3c33ad364e6dd74d4d357b9be3b50a7be1fe64d4fc3746c9593d92173bf13aa6a5936370fd381e23c998cdb81c2fab676faaa8e5975ec98d7c70aded6a76f3b0b2815341f3e72ecd866c6623decb358998c0cfecabb5f712bacf050fb0a2b7c18763a3e8bd3ceaaabd5c3ef2b1fab7bd2e11d7f851da8af70161eca9c0516ebe176960aacb3f030ab3e7c6cdff15755c757edc3878f151e625f01873e7ca0e6595041851e0fb9f7c88ecf3ae13d529061e1ac873bce5aad00701570b87a0a2a9cf5507b0a0f3d5cf31c397ac7f17df4e8f1144e38e1aaf748e13d1ea2dee3dec77f3c3cfd87b4a1390b0f4f6741d63c0516fee3a1cc537878cf5ad1d0e0d3b04ef3f03b8d7cb89de62aa8544f21ecf114ae3ae1aa87dc55f261bd0a5f3df4be9200f0f110bb0f1c02c0c7310e01f0134ef8eae13c7e98fd070e5758e1353854e150f51c394e83431e3caec221cd73e4b8eaa18eab70c843751e0f39ac1359f2356721050ee63d70c803873cdee31c77fc503bc6214c0ac70f379c24b2e461cec2430df71059f22ce070050c45b2b6ab80434d7b0c0e63bea9f0ed6186916c38d4b6d3e0503b979d7bf8612391254ff350079621b2e45738f4a1e300c021c621be0adf6b7098c28f7118cf006282155f5ea0060a82c02f0b20a260e1461286709205e8e4bbce0e7c3f1c1e5f06a3804fe8b1e3343854a5ce03873f503081c611d211485c0105b8e3c7053e08e1042f2f9880040370c743144eedc0610aaf4e7806cb3c9c3887c89297c1a10e0c45824e508fc1e1e93930141bd0c9fc31820a1c548cf0d2e5084a0067fc628989199cc0055e5e4c009dcc9f1e7a3827b2e44f3834e11c0cdbc454631c224b4259814eec351cc2f0441448b84197202cd500cc3094834e5098828d26a410c21337d0009df4b18717af62687158f1b177262c0904095bb42cf0b42d5a233ccdca604ab430f062a7c3ca600851e4d6cd318611e41586143b64671046508eaf2fae642e727c7d91453ebebcb0f52d7b3f3b034f66d918b69bfc36656c1377b395b6b3ef6e899ce127dbcbae4708112fdb6b97cc8cbdd56ab56c0e495633fb7ac2164d9e9686faf5cc4c55f0c59435bcf8fa224a8ef32f2144c9fd9814841181444eca60f3e2a828c76721c01669c81b534a6c4be8c249115e284109423060e1045558c1020711e02012830a18542aa674544441c5cba690486083801c6e08810a2739b06e2aa4603f80e3cea02343174878c10f8ee6107e1084ee07493da0205d0992224492165bacf4200c25d208e3cb129ad0e9020c36395d18e1d5450fba882288edf64d8c2b98b6767bdddd8f40e4cd280f0f335301413d8fac234785a944ab18332d25391a31ac2d0f9d95d02f99d8656c818695c8c4b01ae5a4f406ed1733d1b40751190500ca6e87821a7ef2c309bc6ff0624abb4822e39a782d1a1434715446327b2a44958837b4495c49a143b40a25a24057b2bd2adceedd8608a6420fdbff80de51b72182281c7ab8e5796fd9543082a8dbcb1b992cabc48608ca1066f888a3e2ed6ccfbe612b3bf4a58a622340dbad18d1702841d34fff01b7ec5b967d7bd65d82ddb3d3a218c42ed599c6ec329766d2549a5c7a6531204c082c0873d9377a652fc54104623f3f9a03d9423d4644dfb46851a6c7a4f44d370e0a7d13b3bded626fbd348eca1e03b2c784e89bf88ac113d9da6351a2c8f6d890ad9416d953a34c4ab6f65826a56fe2ab8b20b27d4644dfa090ed378ceb84b8543bda8f488ed33a21744bafecc140ea95a55a62a455d277a547bdb2d4eb23f94263ab245bd0220691d89ed119c9ae7d3fa0c4996bb2ec65f622895c9f7d59102daa55888aaa51d5625f8f6c9295f677c9fe7ac98482b2cd5cd91e1bc3b34ada617e64db0e837d72982c7b9fed1a0ebdb211b456c97ac9b05deaac12eadefb28ac921e5649895519ba967d422af90d932235cc3559f61ab64b9365b1a06cad976cad1276c2f898c371686a9943b3ca249a4546f4d2d9d1a208c43ed7699dbe6f68349f39cd6768686868349af7e84143739d3e0d0dcda5c1aa22287c3b0adbb6994c720a8f63277cfb09b8b3d0bca57a7feb86ccab64a6f99d69f4a81d0dac99f7f8cc7bb4ba87cdb199aa90a9478fad478f1e9f29ebf1d9d1c0ca3db0aa48eaa6a76efa46f329a199e6261ab3d92b9bc96432758f5d0364b6d784554576bcfb8e77dfba6f3bcd77b31cda5a470f0c8976db967b7c3d52667b9a2f664df5f1b8cee4816de9157deab3445588569855a122abbc615511997797c1d74bafe83b2c8774ccdcc29032fdf58221c57c960caf73cb6ee91b69afe4f82c199ecc96a86fba561552ea1b142afbb2674a7d93dda0d3fd61fc35f7b5962c4bdf4879c26f9d6ec09ffc4edbc45fd0c10f4f0e7f714a3e7d76424e5fcc27dc597a45effd2e2f961f8eb68b20bc642039620942cb0896324e8e584650244451bec9118b10ae208a822022072104943432162860649a1cb140d941de60380fb7c2ec5d7edf177410634e7edc391c7e67a281b10be079df36eea3979edc38ee5e19eaeda7928ff927ab8a78b78fc19b6958895ba19c4b9a0656965855447b77ed1dae4210b1654dda4bee12b7de6a2db275dfd83a6cbafc5a97fb9410e1ee9d08cc394f0911efa81339dd3b875b3098c328dcf270ebd4c9af0701da7b6447032b6b5855642a6944f4cd3d3d8f37e4100feb86d723dd70c30d4f62ec315332bcd64c838725634aa6f460d8578680100317c8842a4d31c40bf40141f803d61c081bf0890d6845be0a21331139e6d65ccaad25d3b715294ca5145a4b23208529b8b3e0779608c0a7e7c12191a9041dfcaeb2df3b4b047c291900f9faf1883f24fef72f05fc69433ddaa6053c2c199e7c7c8ea5d6b44857bd128d668e5880a892e3a3b444f03a472c400ce5e8e539e7c4de5f631c866133688c0136ed197baee4e69c73aea8dc0f59e59af597e974179393b46317a08639e860967137e86045c0c5e13d13ecb10bd03dfdc5df2f82d40a0f8de58997bca4e5d064e589e59090c63d58beb9b606843c7da294edfb1ae190e040818c72bd0b4866d2cc2a7b19ea8dd43cc9f54856ed24d7e3a0bd3e073062713294eb5df8216f5a4a296bb2d84ffb38bfc4e69abea94ae297d82216e841a12701b31b22bb18e4bb634018faf8649b31c12d3bc323cf2d91c62d9f3c5b32cbbf6f540c28322f71116923162029d97ed5e2968423dbc7117fd4c77496f24bfc119f63bbe28f6e92e567658247efbd813276198c502f346398c5a16f5a3918761dec1763c740e34c103b922c19c384904164acf30f4119c3302c2ecd1260f141a9e6079018443e66608b9914dee50b928588c58729b9b1f8e025b72e0c34b7483882c7f5ea2cc4f7d723334dafe443f9231b86a54cafb19194e9b32ebc30caf65b174499be731d65faeddb29deb8b5669f659f19c785793b6dd76955110cdb30cd847dcbbe16f7ecf653697808f7fbc99c653cf599a6651d0df41ae6a96fd17be08706fa8c3b865bf61cce9e7f72fdba6bdf4fdebe96ccded9eff4d98fe3e94ee9e9030202023afd498c0290e0138b25a83367eededa3ebb221d91ed16b7ba6f58b51d7b87551b2e726f7f6f3fedd8c5786e03e6c56e865bb6c57dbbfd54191e52bf3a64e3d1bed9ed34d0db73df26b395198b4166fa937ca19be478938d0b3930f44dcc5b13228d27abe445d9356cfd8e47f68b4751b9754a02af51a69f9fa5c26b5d22cce5d1dcba2ea26b9427a59276b2a8ca7a4a050d52a79cf7d37e3ee7daf93955b6afd06851dca5bd8a52266cfcc1164f63102beb6d906d7a65678bb34a5e8ba3691862904720f7f53ffaa63e47fb6a5a2a1782723dd73831cdbaaf576dd947336ba0d3f693d1de4b831c73afc23e736715f6f8fa3225c7d7172a39e69319a6e9d5ad1148349a446cd1a2bc215a963d23be1f6d49c3867dc3eeb7d96f88963fe34d1fcf7da45f86728d40bed686f4effb59f6edcb1e658eb8959dfbd6adf81881c406743d00f9d0c4957b94701ce69498cee3817854254a10d9842f900faeac33b7d8e1560f797ba43cda63d48e378ecbbe7ede304f96653d32fe70924631e51717378138b6e478a23635a7b62bd7571c59a2f499946241c838769063289db412f1729df42da4e73507dbcc3e3117034f0e65fac8c6d4be55ce54fb5c5fd3eeea1a66bf695fc5b21bd25dfbfceeef76facdf461df92ce2d7f7126bfab0db1f2eb9e6554bcaff2c4adbae3a1aed3f3d3a6171b01281c32393d7601b487b10b30ef7d9e7634ac32f7f53d2bdce5d7e116cd7488d5be7df76234b072ad7828c7ccb55dc14f5e423f89dc28e1e17e09a561955b4c72ed21b7e2e57c1c4ff27cc301465e613e721532310f17f3ccae888f3cf1295e0e2de948bf1e14b20cb37d77457ce4d6b4adfe8c5fd27916590df52ad6ecca656cf7b7d58f19cddf6c895bfdf87b9dd91366f92ba594efa1694c4abca4738f7c23db4e08761ebcac037535c9ad7e0ff646932f99878fe7e36eb7fa3d17c88726d979c0b2077e728663ab075bcaf53d3ba3dfba65fcd1b75994f2b295613467ddddb8953d665f0cf3c53cfd88e121f18d7978e21bf784b965cdb0a1980a262345595ad3ab7e6c967cc7cef1f586520fbcbec7de5873314baef80d306c1a4b1c450d800347be1125cb0698d46882521390dcb0c276f1bc64792e85c3042bbc78e9550c13a4e4d9da62df44491fa7e46e4094dc9f5eaee4fe7cf6d9cdd0d1d09fa71fb5c92d6993b7934d965d0c5200590a00e3b4db601d0d3cdaa73c67803cff75433479bb23f73e17fb14e9cfeec8bd7d22c17b9f2712b4f63ef65887e4dee722b9c7a89c31d8bc00e5f9d0766d74c9f3d3a28ebafd7eb8b7af1f130dac8f3108766b6750f66aa52701b9f5933dcb85a7fd09762bdb5d1a584f7f7acc6d70c9dbf6eda0e5eddc4744339dda64ee2d1e1db673ef3ebb21df0edbbbc37cad9f8ce2913f7dfe031efde9ded733bf7c994162fa13fe80975bd4e592377daaad6b2389fce5f862234b1b44e49a1c5f6c7861434965eabaaec3b8ed536da64fb575dd75ba765857a47b1b457949e779ac2ba2e5798b7fb838fc0135fc04bb042b95208cc3db7572c5a1adc79e59cca4e21fec4326f716071db4bfb5be1eb373ab576d94897743af07acda5eeba5c4aaadfe0ce5ee84cccb8ae36562fb9241fb74264840ef487726310d73ee3edd611ebb1d987338dc11917487f93824a10fa8c23dd0f39939771f6f67e61c0e4df7bccf5ca7a7ac1125cfcf9d1d479df3d971d36b77038f77b7dfce8ea370687a4cf6ad72af7968e2f10e873bce1d4c7d3edc79bce6f3e1f11d8ff97c42ee3b9e7dad9fbc0650deeab7834644cbb5728ff9b6c9ee06d3637efa7c4c3f1df585a6a3ee7da1e9861de7719d8ef9c2d36bce7d11f3bed8dd109e9e7aec76769c070ebd773e3cee83eff33d64e283c2611ef3d00bff90077e88ff3df5f98421f7eea18ea370987a2853f3f98461772fa4f9096fb0763ba6a35e3b24de77e0f0073c7de60b2578c2f5db31d5a3b08fc7cdee08771e18eb2cded901f385dd4d9889cfcc6b8e84c33ff038f71366a281a8771f130df48e5b36994a90e91c66a281dc6d47c3bc3cea3be9ccee93e0110f87dc3bfcc4742b16f3e8509fdd1ef50db76ce4fbf3d9b7433d7d3ff0380a87125401fb84aa873d1ed2fc84d7fcca63e7be1f78bce6335f98facc374b82a11caf10b22418ca760969e49845966a78ad54a6587c88927be488a589ab4b962e5732901c5f5d80d64823e35c61e34abec9f1b5c61b5d320d9296dc23c7971a55b21a5296f046de38ae7e2a0c17c9b2bdcc5ac69472e48229651bb9644bc9b0e2ca070032a26458c9366f260efb541513b1cff2cdb123d242172f57ee881c00c88808a612647a0d0e2518a680bbd3438c997438fc70d0c1136682c2b91f3341e149500240273fe0e9a7c79091e54ff8820ea2dee3a3f998748fb90a33f11ee379984987830e7ae7cee30b7fc01d3d09c83cad285b68c5971192b204535fd0c1f0860f830e32319d3bea335f288399984c8f79a8033381790c0e3ad83d070e3a88c24c6070d041d339fcc3e9cb972f5fc02ebe8c8094255e42b3bcd6d110b3c81277c0bb22b72a15a2ac433ccaff19ca596703f7eedec4e9705077137ed2e11f12009acefdf4b7b3a13beadec441e120d33dfcc484add877a7c7f007bcbcc4cb3c15f340e0073c35683a65e2dd50ba5c5e0fd108abea4d7f72d074fb56926cb1aae21d4fbe854396e74e53b285ee869b5ebb06783bddb9cf6e87bb09873544d02439cae3e7b60746a392a8144c35c6911a668c219a0104002000d314003028141008452291582c0e95791f14800c96aa4a664293c8c3248b51c818630c3184180008001010982919ad02a9650ead843e9fddea2d6141b22cba200ca710a34df86b75054f8b4b534b29509c87fc1d1e7cda2af627d19c15efe32733f74871a2be0b55c1a552728e0eec605fb8519282722a414f6ae02c5e3198d7f0f58883942e4e243003f92e6911a4ce19d0c72301a8fcc6676acbaddf26070057e478e843cf79b81cb6427e2556d385cf23deab577cf622d7e128dd5de3a73fa7bb8e8e3885767fa392a6e051518a3c3ed3e0e011d4878babcf19c2f72ffaca81bbef50d5e09e3236162ee5fa5e454ccae07c8be0b47575d4015decef09627a8b842b445d3d380dbc91514db9e8811bd53dd4562a555ee32c67347b4073c341ff21a984c1602ddacd720b04d8f3367d4ecbce4cdace383f315c5a3b5abcbdddb38cee2e91884143220300eaadc9311da26f9437e4aaabb3f6fae12a951b88367f68d718b2177d7b8f2acbdea1b5bbccdbfba0297cab8fe68e8aef3536c9214e1200878f97d192d1e8d8c64f93aa3ded5b2cd87bf0268c6e22707fdd53a5dcde5a73093e61077960b23b2a37ee4e68f4f2fc4950793492c8bf9f657cb67e6e8b48a3093da1b62f0c281058a65d211e132f70b94501298a0ab12066b81ce8574700a690c0afd558b934750e5544632ebca68b2aeb3c8870e6cb73340d25dbc6e723a25ada293ed67517f3a75d270f4ce4af3c85178af9fc6819bd06845a2a2dba50dadf79064d078a0dbf1d48eb81a27f92221aaf891a1f1cdde264ed049e0e06aa37b75a371634980c4d78a1d2ea45f41ff503069bf0f24b34f8d1a1b9b9d43e999ae09aa02f9f7632f8f81c8c8c7f73e752de2432316861676ef70d977c3c5347a4ef9dd3429dafb224c01faa154ede2c495aded9f2c05c870375911c28c5459cc824d8946e081aa60042fa8b54f2d9243684a6c556e368a5cd74d99464814d6ee9231b51a14c104f52d4ff43052029653945d77db0e62b988d80bc9b1ddbd16dc43e651fd71728e7c46c30d25ccb49a055752db7bf99e9ebe73bbf1706078cda2c5e118df22cd5fe0b98961af7a8c3345af98fb1409033feb53262e0745cd258bf48af716a9774962c718777f9a81054571665226e04600709d6a10e6223899fdc47d7693f8353f6f68a109d322fcd7b021521b51e50b417ce0392c59c7740eddfbfcbc075e7e2fb494cc77af2dfbebdf3fffbe0ff84b5ea3fe81c866801ffcda7788a67c53814220ef9e82b0d14ee9a481844f4f441e2386f51f33dddb29f0a37386ad2d76841fa99e2a5d5db2499bd562ad2ef4988b7e3001df2dbf885a7f3f2f71dc62be77f0e20bbe9dadb601e3768a68eb5b6dc189cde9e6aede1349eb0416000f74c81c07737a24ec2b746f252829b835e12000d024bb34878b0b6f412359934953da0f22cb86ba78df20945d1ff5d8d6dff95003ff3ba1db4bf767dc4c7194aa8a4573a2b283f399aa2835ff31464f3041df6c34129b2315340a1446b6ee2e3d9145c6e877b804c379eebd91e7a0e884faba849910408ac2d184e03aa925d34830ff0ee9352df13589ec846db5b315d7a51bf472f91ec929c620d66850c9cc596210dbc3137396c82be44ad589d8d41cb10de3a2773b476c232be7e53eda288958c65d327fea695e0a4e6973cbe0183b631ba3346e5671d808b4551282e74d53aa6aeffd0de1d33729a531dab19092b2f513d53665e435b3ba8ad4978af9d900622218d4950697d08fc23f7148797ad8e50b42576e4b92e66beffb7883350d615eb10f4294d89777a4b84eae1eb7cede210b6bc6addeb1d696a4b3d1b13e4f9a55c6d5b77a922bb2520db55cda2a2e70058767fdd04b93c74cfcd8e09de4fd38669e53f901f751c4937ca50c1b434c1b2ee1b90ce89416b1865e4cb18b1b71d1b2e7a85dab4a288c77e49233487da2b0458488755c7bd332d194e17e3daa0298d9244859c0ecf28acde02b712b216e0e51b7ba1e6449dd83b701e05c2d809007d52e91f5554c94b082acd02b6dba80e7211d9215ae24b34c49fe26d34349514c5ebfa3208724959413f327ee36b30b3b4201b12ccb196beced77a1502de48f8b0637776e90676d449a561c746d96541203a96d7284548ee158d022f147fc0d6dd0f72dc4ae040735341a7a9b602ca9cd7ba8bf89bbcbae5dbaa47ecd297307679b2b8b3f3d825e6999256c65f86b1f06fe74910eff31a369ea870c4cc3b917ced6dddd23866af1d209cbe56b4d12e6db824d7170a80c1fc25defe5221bce55393f415f1403218c61e28e10ae8dc1bfb6031e630d3385a67bf1bb2beab7765fea18b1b425312d615b4490712a3a8f9490b6d07a604d5a89162d0227f70bd7114e0d67bbec69c34f7247697930640f96d102bd39e0ac98ad2fd40544728a0c38b6f6cf4d4dea0646d2088f200c91c8a702e7b39665a7feba126b8f927200d4e7cf48c5ed094a8e67328396443359a98914c9b6de204ebba70cacddf31f907f6ca259617f5ea0da5e21485ddc893e92d99656cd178c6fd67cc516ea678ea667dc276781de782becf5db12ebd8d1eef6aca34b0af5d5dace0afcfcd0e8e6adc40be485e379565427f2ca3699c13822ce583bae067b5013263eae4b977b7819b3e75849df74f1808e6829dbe06c32656a7aaa56bc41e500e17de6b64c5ca514d33bee0039ae370916b2f7e19f04078058376f624745815cb825c67f66120e0a2e68dfb7d79d509f509479d2a2458194653e07ae3ce60b7e956514fa317a9b251cd46d461265d60ac23c5b5ed22a9ed916a9037394f90b38d032d2fc400753db1749b547cc503e6f7373a8354ac1351669dc8554d0b6bfd7ce6194590f0404e5cc242df0d1fa2c9f71a695a08a232267c3b7a2d02d9a8ad65371d326ffc5b14f9cdb0424d8702e1252b3cdad37c6805e1aa732b0c37bece5554d533781fc7059160b1c8864f0f0a610b43fb6107ffa52ac437a702c7a31075e6082c66d7d92cc959bcd9a146efe8fa979d1d8c5090157b17356bbb21f3dd893dfa019446addc4242e2d1274714547eac53fc758631e152a46b44eec9491d1bde94b53d0342a9b41ec5f4e9f410b51b3d213ceb3966dc89e7be2cdbb9e90a033765decaed8ed094c969055859ef48b0e4f2085a90929960a7a08894e353bbff7c8dd5d2c464ee54c0020e18909d9afbb40f6ac87b17eb8f1ba6c39fb8e58065e2da4ac8b43701f5e3741e60df175e44a8f848007b444e84264f8e52b380a512f936f1dbc102332cd8ae304e28d8db6ce69a6db44e0f5daa3e79c2921f9a979f6c3bcfaf7475a7fc8b51a1a3589a1171187fa9aa7872df1cc57e6d8b0446471ba20e472bf8e00b3fec6e697186c5b8288481ddeff12e0f9f95fc1708ffbb02ec26dc3dbbe68b09706426c17f1e1b1222d9c19e7747d89a71d31a31b56e3fc1a89e4421874093fd7af043c3dac21654e4908c0dc1e40105d6a3ff31794107ba1be7958d57dfb8a2a3f8625a81b6730bf2e4f2c50a59a403ed1a7b076f7a2983cac3a614ddf9b251190af6688f8afe09b52af52fefec3930b84c3d9e4b228234afd0855aed8cadd329c809abe21a990ce6bdbd270beb80b70da440ffd8508ff3f6b5dc0d1557f27cf8ed413a97f037c69ad45e360bd083c2ccf6724bac0c0780f8ed36292e5d45a045924b86e200384e2487db37451817e02ac2d4b9b9dab58fd6ba19573bb32ead29ed528c00f514f25f57abc0d4545796d57b765fbec8ad56e2c511b0950711762654b13efccad070037a6a3ccb6923cabc560b93bc341785ec2896a978db6d9ec962bfbd443a18a8025aac010be503bc5e3afe8cb0eead5a0403236c063524c9b019a1f4cbcd2eb82459d8abc070ae9feb92ba5fcd769d61e67184934b737d3e06fb503e7fa5bf76d03618db30e432b12295bc9053b3249108ac24fa243bb27b3e0e92bc926235a342a5c2827adbbe464dd8c6cbd49257a5b7efc94448686ce92e68a64ff98ce75b1ca7c823dedc8b0b0036e38b6de1c48dd335887a079eecb3245d450239fe30ef651f92d8fdf3af8f298aa1a2a0106dffdc553057dd3bb9d6b5efad94e6eb65782772ae4c31742e965bc42c541638279255a2f29b3be6c1565dd2fd924bc1be32d685066f4277328d75969262493157d87a141768552b502a669e88a2be689f3664e32dae2c5c71029667ce86195cfbe0384ce30284944dde1351d7193e494122adf411d1014c1cc6bd79706978f5a7c17e48d4ab50eb5ae7e3da8eb8a932f332fceaa77e9e80fd0a06f7dc5c8c7376e1d9495b0e98dccaefc466d5aa4c15259f2992f186ccd5d331e088aecd50bdc26121e92fa86d03433a57bbb62a09d24fa4962660ef60ae3436ada1f0fd7b5e193dcb1443a072589240f48cb9fea9bd151334a1f03a1a0c505ba58901b8671877cddeae11d8b3e81d9c76f3518da9c5db365c74c4ee9ad371eaeae2806070d5301d5a13d8569e6b76bca28a5eaa9f23c61ea79c7d209cec31851bf59b3e7f472abdc0288feea0d4b6d33691d64bc5e962d65f67f298c799ac8b344316887a1d0d9e907d6aacbee8f5b5d06f8c66e2a3402f83bc520d03badf88d4e4e42707d95501679b4cb234c2e0885feef82df354c393c81b5aacd5354adfeae0ff09aac0e128557422f676950a579cc8449ef7244cfaf8c58dd48326e22661e06c4414ce362ae03faed44caea93833477671c294143e11ab8b28f225689820df7fc7fed289278471da030c33b4dc0821d06f3371a0f38e0b2927b704ebce0b49e67c0216cfa39f3ad27e6f0e88063e180297a8376a18698466583711fa375b1c7a834e703d0bdb5b30ae9d074e805c3ca26da7bb455ac9db18aed3f4c5aeb877a72ae3db38a8253cd631d674e6156b6fbd443503d2bb4d031053a2360c2af4b0c22415b9d881c6aeaa71796c5b2cd5b5a0001e6e96ef0f012876f679eabd39abbd6ceddba1124bb061c2450fad13c91947d3680850ff548b2aff0039428743a5e82c6a22f6c0b53272bd54505963503bb55bbf9cb4a1bc96f91862c3d8b2c3b823109c90d2cad2156bd1c302b7e01a6bf81f37d1cd5c41f52c80dc54cb23fe38536845598a311cb5fdf527f70c64d505ce7dfe30a8d93ba0d59bfe464ec51d81896c121729908d5305d4dd92f71308e488ed227d43597408831a39d6af0d0eabd91c3833565d5008c41a15b997ac07205ce6480349d3733550b5b8a66ca39436d701d1dcd020fcdff2bce08e8bfcc193631e0eabb9f39870a0f1ddbc84e01dc495a239dd7950b4b4d209b91d3549ea12f031187221dfc5355dba78b304ca46f1489b2fe7ca8867cc0da6ddddba4fba35f465435adf3be210365f1002dfb600947a6a6068ef906667e813f6b9a659cc7e7838fa00de959d6f1a0243db6069c3e459cfdeb41167d44a758f8c7bd671da8dc60ef468b5d9949c7497b9e8cbc9b0c92734d741d7a2599f90b9b9681da7116a82444b8217a854006d2b6dc5d06dfb3a2617fce000054fff8cc720315276bc5a998cef52727881ceb4ea5fc28cecbf5e5f9e0c9bb88be369bb60c323c51fdab623966e7489d5b76488ccb70e891697e3e5403b11e1478078c32ee0722d3eea21be7afd386cc2d941d540616c3600c40802c0d100c834d245d8a7466ef983efe4a30c2646b6266658bb0d3559f00e5e1755483d6e3b86d58683013a9ba38ab37433be458a211eb675a26973e20845ba7c7207537cf10ad6b10d18c86ad18916a8d254a4a7319a49f1f73d602ef6c6212d6ad96f37ad509c66562a14c8df03719d6169165d87409c49a7c65ec11999acb0477d9f5fb305fd24b69f09e4fca7aa4f20a1e0b61d450593e9a58482c7fb7b14665c0e417d161e852c131c752424f7dff90159e313dcb34a945da3922853af103bec2a760ff5972afa57d2177001685534d6faf1e26bca54bb6a11c714573436d2d91ce09e68f4717670d64affbd3aeeef53ec3cce7186b1f77f89c1d8596c5530491e8ecddd45d3457b2176c1f1de528c1282169d4c8631eaa4f2ea036fdf19ae194ee23b76b1fbcebf105e264e25e2a4e2b6da45c929e57fb8178c5f7d2fbb4bee8511133d8ee8a5a7cbd55877bf5ef9d37b1c2b669fce67a71ea136299887b03ff4ddceea69d1bdf1f5063db5df20c1008550c9c6e7d3a68871ee077bb7f1cf9591a6bd42adbef3af46cc3da0fff90c2ea82a19b925d6c2f936383384d6d94f3e0f97e2ec0a70ba0447c01d717ae4ad04a53aa8dd9388c96c7e9363b70f98f954fb73a0d950d39f6b2ed802de1b71dd57554ff5e702150064b432f45668551f61f93e0658f58a23f5b991392ca15620c57dfff45519ff5aa528105b822cee6e6a9d99494f9e1546c9aad0b7e31d31ff4281635d0e642661126d8238cea0cdaa619fb8d1202f09bc74e241ab8804ee594798203b1ed8ab9723d417ba86d9cf517a1bc337ca7942f7c43c7e7ca16e3d9a7fc7b494ee1b80390ad8b83bc36c92ab05c07cbc51520905628d97a0a76a899a6736093973897ba1009282f7fbb0e49e8f867b8e5d184db8c714ec80348945c61f38d0c83d606cc1d7a986e1ef29d8506c376e15531f1ac305024b495a57913a2dc4c380e9408e0b119c6be76d20f66c9b7b186b5f03d8b89090082e37acf0294de800950ad2096e18bc13b45ab29bd09274364b0d1024977e60598818e817f284735171d38c63618506fd9f5d4ba6541c09f1e35950644ea1df0c0addf5d01b7c1d29c4b5cd64cc59348302cfa0e53b9f2a32105c4e12aa411d9219239b34801f3411b255c11f27faa20e6517c72203fa7f9cc4afb7740e953d3a4f874f9e87420206a305956ecbda73763382ae215ae8af2e6471290b0a5c3b3d4ad5b7d8d465f3f8be427a803f98125aa70551014938fc3e829220a7a78a71896624a4cf5b04ce02a453a0bd72adfe89347324252af3a1b2b1cf8d87b0f275f664c1da854547ff5918fe1cd1b0e77f463278d0d3cc6f6ef0033b962fdc4bd539f2910afabb0ca371231d8619d7c7a02cd11d37908286bd8bbf8b8c5176a81ce40bd8db6a51ce20699d9dcedf5bbf6f31035a9ff606fdcb68e74311881d800d8e8027e99b158fee6791c71e90968772fd3ae6c4923cd58f2a088824fe3125583a7674c0cc2f47ba4e99396e0f17017ff91c5f254a6aab7ec05f66c41c48ed869ebfc8ab3c000d6847c6c8334f37c9e32e2f315be0513d9a4ba9e46a7ef38e982ab01b1106d0ca0b12364988ee8b0233b55b934030d22444a83372218561603a939c2c13a52788bc5064ad20a09b5e96ee52586fd2ff95230a96c1855c4bb978db7c2bc4caee635472a50b2c67b2c9c20e4562ec22168ddca7ad5908135c4d0560e594b36272b6627578d75337361b5d364bc00ca589680241a6e900651ddcb96e2e2e53f8dc096d3c3e39b58e5f0b55a5840e686c1eebd2cda4f448215138c91056c3f1e2914ba0732ac0360172a77bc8231531ab818c1de69082d26fa9a33710fdcdd097cc4b29df193925159fa02a022fc82810022da824df499eea2b7ca42d33f4c4d7a52f33d810c142419417548651f0df5dcb0cce5fbefbd8210c6b892430e3a551d64062e2bc1b715d339972a0c2ee0aabe736888551928e5a77762bca4beeb4806669393ad37d298b094f9dcd4318dce90a3f47b2bff288304a8e561443329b43d0e73b5b582d63f1613010f15cc43b645303867ccbef54ed3e80ea0a06df34bddd8b1fcea54b6eef68d798353c4ee6f2a276359f41c981bde4d81abb66693aca3b8d5c2bd37be5443eda8750762c473641ddc43acb9590cb6c3c4567f305c2152fd62b66a42324c86bef68def5f6c0e541e8cf1361bdc604075b251a775faec49a197071f7775092b7c2eaa41055d4e673f54b149807fd357b1b841762f6ef6caf8ea15f32d1a75d703030ac97017a27bffed3e66abf39de42b7e9fd0ca804697a3cb0b842d6c7d1df0036d03a2c83ddb340ea003a617a9173fb382dc7f08d54378df41f33b2b32f234ac5b3e5dae762ebe73950ce68317794fb702572072021e230936ca21a55a4507665a191ff0705c1c656358940b5f79d1b94d5f73c03f644356c230a725c05139dbe1ad3d85eff67749285937beb09ae59998144680e178bf410d4405dde1d159870847fa8c5134ccd15d5373c2c3b6f764202d25b66209aa35eaae7e53bce77dba025de81d557a3b31443f46b99a659ba7389b5df37dfac0141ce6fe4f5b91b95e3baf6cb12d4a7667ba45c6727ec9b2c7f1f02523130f85fdbd76c307e6c3d4fcf803a114ba9a9e19587086a198b4b2fa76349028e01bfceba1a15982deb4652ec1018e2be442122da903a21a00c4eb0b3bb951464a71ee41d7b6db90e4bd1c9a4837302b7ab2911d3e884613c011ff80ba1f5325821e592f075a3d3c19a02f18a177df6447908ab2c9e10f61f596b899b2f6897889cb53adbb65cdf568fcca526eed10f56d3bf8d880fcc86eeb9ad88affd11409d14268ad566852447a9249da46824a6748040fb05cae45ad7738d6bbb245c5fe8e450605128a11065b61d47c8d4e4a794638f2d319b733625c0e68d0392732e0a5d07c5db058a98908e4826157b55dc8213ba7ebdce066ee10829e95740564a56589b8dcd85323175877db384ee63541658d838de83158586f6253522fa986f9032d796e00277fe4936200c811d1aa1ab59e84d0f2b4b00e9244b00dbd1a7fdb15eea02d2f7594f7ccc13841e281ed0b65847ff6c42d6509b831bf16ddf08e85eb04552c229a7661dbed9b4f117f9c1b1e48d5cbdcfd2069ee162fd40283ae7f9041954e2db6c7212be717bd3e5d25d0e7662e11bd97ec58c6e145996abdaf04a86b29650628fc5b8133d4c22a26c26eb2763a61147ce839ecc17b6bf414fc8bc91fa1b4c848a211e33ff4c207c632e8841a2b4067a8950284349968dd2ae3e13e8cf835181b40af328e33806cee366158cf5f97420322a83860f98182bac7051756d8852472f610fb0a563d43098ecd5f534657d3b8fecdd1dc2b6c7a316c5859fd093448db5f3d059c1c71971fc5f921440280e7c77a61aafc05d01a17c931c190834ed4034665770a4a1e1607134f67668cb2bf6e46e8cb84e99ec2e71993ad2f1a772398385305bc91c6a1d886048434a96ad65722131482200c0948ce4477b55a793940f46083af91b357dc2564b1cf949ffa9cc3b9fd842896dea060dc1b63c458f0a836639dc9a2e47fdc5196e6db1a81a8cef57c6567210aa42db6e3b4598b09c39dd4104d0e07838a0771a084edd247d4237576eab3417567e9d61e8854e3d80f2009e0522b5467e54e2ae1ada12ce80fc0a1ae385f425cd980278d21fba848c3fcb8122dd10bc36767dcd29469aae990de96ac25d38788b2aa67953234b1729c12788b94b4a9a075e8c8e85d79e54cfb716b9979917c97a2d447bd89ecb97d78aecc004af488c888ca5383299d912e451af6b237def03eb3c3887b21aa8fe291e494b4dbaa57cc40fdd96fea7a91a6b89d993edb56b6143af25fa739bea3610e42ae1bc1eaf25ca5127cd050590a0b7c63b9c29cbe53b8e4cdca218244cfceab03c707261b07e8667d7a2506cca458bf91d68e90e1a792642755ff74b3af806b084c7610c8732546a14203f0cba1cd6bd44da71937b5f14ef82236636d41da0bcbac16be411df7ee8bcee9d4b042beab81a8146c26f04bfc10c00b984b4c551d4e0b98ec5c3858d1668e0065ba9ca35c562743bebef93d76097db2a60694c80fa3cc3eb3d1384409cae906ebe235203f2499af51b9a2e892debcfdcdd25c86c882cdf1a119a8b5b44acbdcf4a6a2cbfc69ec2b6eac60a29ece33f6ed8c564986d239e71db78124424628649862835ce65c3ae942efce7f129b8046df7ab320101858f5043df98b543d9b96ce1462070345e34ed544786b505685f19593fec78520b32c1c104f7b2c11c9d2dfb3c8a779211da1b1562b83d6f2b84e998c99d87055aac36c215408b91a480e8b48217f2c1c64083939ecbd9387226a4a9006b406e0947766cf9c33b2fa2bd9bf337e17bbe7d7cc1f9ffa4b22716c020d9550e9fa4afde81240066b84bea489292208629480ef7e1524f8aa6216e08a919c970d161f1369ff056d1d7a0bd15c0e7b2edef4b792e41ab158412e2d59821769e10d0d5d79d18c9ce03c9e1c58bacc5eb3e4227c17742deb99175c99dcca5d97304c9621256c9bcddc3e4f5c9d548d52a2c28655566ddac8fb3eff89545602a7336564f66f7613b95aeaf1c6256de6a7644e976eb0f0ba379139ea14870e04c092bcb0b13c079764c2621319bb31e2467ed7a741c7096ac13eca390c8b144c6711158a31a8ee00256b4e0bb2d89011844571a1f2c79e2d2678c748780823c79e9aa1d6c5f76ffc650f20e65889834ec332f1ffa83250ee03c2c5e9c058bfba618043465e7c3229ed643c7e32b30ab57f4515a57a6c3870587796038556085a5756d437c298bdab074e23dff135ac5f2315032e2cfed5f21a6c07e60856a9cb2e441c3b3345d38d3b3c844f7ff6cf05581cec31da28410b3ae1caef945ec8debc63ca2de8e20f526362122cde744db385c061f443be0a298692a7de0a0d62ba8c2eee05926a9888fac08376285c1b901fe39c6fc11aa398314a3b88cfce1582e7a584e25207f2710bda0377960f121fa97c592e13c733128480a1900c3bcd21edd3db5fe288e7ea2791598561038ee1cecef5108eee7d78587c583b4f5bcc85257a79d99e1dee2583db4b007d43a9cf6dc2f627735cdca1b1871d10fa81fa5f6e80fcc21ef302c1b15708a179b83ab8fdcac880dbda337b9113d3dd4c12d12ec98c605e406da9a6c04485ce82453c8ced84fd01b6e62f804f3e789603c23161e0b3273ea8715af8753a6d2a599d2922708f72cd97fec10c2a8c16ab6f03c9374da94b7cc13613d27761e13f45054b4ca7238cf264d9b685a63e2f1ce51ab470e798a16adb0128e324b674d4bab3c618c736ae37181719b9e68d57e18cf22bd36695a633634649021e79a89bc39d76a1a35ab0fca4199a50e9cf11adaf65c7d8122cdf4e0a91f0177099aebfbe7f8b4813d81b3c2ee877daa24cb16bff38d530bee997305137a940018b16a9b6831e186390730a12743888c3be6914ca8f421ee5384a08ab2b0e3bb5a80b72e1b7f51be80316eda7fcc3b4d656b1c3f502333a311c6b584973a2a5d7770349d8dad782d630c9db0b162ef33ec1c528fdffe9d59c57fdae5d01d80daf0925b9b9ad5c36d80a95a1e77a1c61e621b53d066d7eea8e37aa2cde54a33a4c657b52e2ee85bf4dfeee676ab03d4f89e233a8c8b388c572af19a8c3da4f221f09c7a19c5cd5dfeb453a0f3bb681978992437e8030a60f5dd7301fdbcd754fb173b87e94549969d7c773c2ae3a014e9558677f56b122e6b52bae19cd011625abb79de5461696cb5952cca69ccf415e2fbfaa9c26e78ad3cddea162e819833c18e884a64643a4a444cfb58defbe444227765768285c7a4a219018414f42314b3e5a8109b7f024b931a984135b59d5404053848175a9f7411709b2a069108aeacc19190b8eae462af6ae04dda269693ceedb8590396f28e6ae692f2c8851b4ee9d06d1ceb870def04a70ea3082a66802bf0ef9cb252fc9f2314c4adb15f9f261e9bee73bb13b2dc9f2f12936b964d6fea29116159f5537b14afcb768f50bc2d686b24516b81f87692c548d34161b60f87762b88c96767528298263d4c9b33e914d4b4a1e4f3055a006b76b65caa8879e7b3a42dc6bb9ceb012431c7b4410a9c2fdf8d582858634fa242e1487807adbb30917d5f6b461ad3fa4288079f4160651026bb218093c6203b9c0c16e16c8711f144571308242ba7a0dc4cd4b9a8fe8150db576bdd657f3a5e7006b1bbe2cd9b729f1caf79ef30b06341e3e283b0019dc10a83a63f578cfcbd033f2ea6d6c4c48ee15681c1e82abc561376b9572b18c581340f0b6758bb5f1a5defcd590a081d37b4fd803b454bb9dc178e5a271227832c87a8b7f11f6f0ac559da2891d68fe756a6b104e9592a9a4072fd191b137a7ce3aaa514cc174e3480ca520fa5e963497a208a20c9de7e7d4c7dbf5c5b31c0312c02b67f227b8eee6abacafd5f363dcc8d820889a14face9c38a4dedc10f1af2d0b0f950843d9600e87bfbe31862314153952228ee1cbcb57f6aea616235dedec23d155450b4e5efec214635dd6d3828d24a60c71b55697e3ce02d24a139dc231084cafc69b9fe1f4e4ab00210c25c7ba3d53532e989006c4d06a756e3169697897a7a992ebadd657c0009b48eebd8fcd55a2f40b82e7a29cf1c006c89d1c18000f0cc541a7b513281033ca02dcbcee0cb06ac77aa77067855cb8575373d0dc611143c6b3005712e72cd0623c4f63933929aa34704ba5c4aaa7f61afbe8def893a327f2c0581b23190966dc238a72cf7215305874950531b2a7759b5923d2cabb1f2dfecbef8acd100433c62fd138a24a8c2505956126dbfc21ff031eb55c494faab1af8b2c8d0c0c18011090ab45102c3cd27e1c7ec3500a06e9290d3d1c20021f92934a3ecc34d2f5a838c718ceca126d6b53a535e33bb4d2303a05c0fb1bbc141b3d44b4d5520757a2a71d2a4facaaa89de265bc1e3827e74189460231086e699ab6125480906a436920546f2ea492a4921f12054d13ef4b02b3584079eca37e663c889188196c236d10764080efcb29a5f14adf02f5216cd9804ec3c12364f02b71b7fa82857078c226986339c1680b08bfbf906e4f503e83960c119957c4bac485011522d31fb34bf15a28039de7e9a8e3a9d8f5df6df0adce4a2c88d02a0ed864b6828495bf79736801439653bdb3efebb670949f41d66a180b4b9ca5608ec3276ce6753db2ac491b064512a01fbaec5297ca247bd33014fddb68e0ec7eb5f023e6890f65205d69a3f1bb3fc533f86c4ce65d9e406c89426b9f40d981ad4bf98fe9069e95f345daf78828af2b934eaaf62a7cc466bc5b7eb5f3caf158fbecf338d1c3e53e31e432db0bad07df744ca5b5cd880855a457e3aa617723ee40213316d641d624dd816901ff89f2b61c08182700874640e993003e42c2872d4f387737073c83cb7bf1d73e9910a3caf5e267956e7a74e7bfa4f343c4457490955bc9051cd88445c678bfec3a57a4a011fa3ffe24076c12a9a13169065777b00bb4658bc4d71f4bf71d86f9f014f47ffac5b8c45ff3da004082948a3dba41de4e1d77e7bf6cd24af520d3bffd6c245516ec147a4b9438b1f233e877f4b0239ec94b1eb4d39d02bb47a3e0e4b75bafc3c37d2871fa03ad6f42057e8835b80d545944572bed8424bbb85fb1ad37e12d0131198205aa6e17ec00d9ad78be66dcf9538beb44f66ad9a162d00634866118e75c54eed81bb6a8d1345beab0e901c2df1232d7c7647b6b2d4908e8296bc42d8e4a42d788d14ee9c1160ec7a7c24a64d2b948e795e8b7e639b8b41e2cb8bf30f39e89e9395f4d6b5c9b14732dc418ae229fc056502ec8493f5423e9e964b4fb6389534b61ae35f522c2ad15ea752df6143a1b5406ee0b062355243bf1b3d1c9ed2030d1107a9e6dd897896d3fd085d27b34ce7f1f5d8940e5658ad1b33464f7102641e4761c14b551bbae2d82a1f069e4380bb3c69bfecc7af2f715b0c6b049b501f062419429e111bdfd1846ba988217ce5658c1857578a13265f1523dd610334386509ad8234ae8020ff3e5be953c084c2644905de5530f891a62beac380ade40c329e69256ff6023145158db0b6af0b91c4c36deb2dec0b7f588b50c7f05245c7e1c5e41a905f465472593659bf6aa0f2598c259a7d3296f9aac10bc9db2ccc896e851d3529227253eed14d41e34d11b0073675e18653ea8366990d2d330c9d3859af0e75c1444a2c214affa38c0a45bd91c84282225086737c09ceed77bcacbb707580155d0b3487b07a6e11dbb2e94dbc401b199ecc68a36cb8cd460289bc75fb424629f7059131ead30ddacb3965fb620bdc4d4aaa1ea6c6cc72814d2c9c1328f47465960f31b5f89f014652be5b86108ddbf20448537f546f5fbc4f70b3219301e3e28dafb884645ad24604b6519b94249a327aed5b78e350dd816db75b5a223dc27d9e463d47e3df81c5eb1c95f355d2708e90152c6f74cae47b6b526d01fab2a432f7532e0286b9e2fa04bab700bf32bd6597eba91382ecbf75ef2b77fcdc2bcc8ec5d7969095182320796a485b9c7d52c8f91a7b7bbef8c24b0c0fac0587042172532d5ce3801a8828fa0ecd32c45cffd605aaf670b1bbadb4f3a1492457da9e6166dae26394b4e816f991fcd373fb4acfdf0e828a369a4575f8daeda6d596f8b41775e3381258a69d4d1cef9ad0e1002c05ad5ce31059b9490f6cef63d17972b1e92ad454797658034b1ca9f01c4410b7f2460ae00ad380d3ce45d124d9100bc8f4b9872880bf61886562d432c7fadd00170ca21ae56735f27c4ce9341e5f6eeac9be405ab7b14828a12e9696d8e1c6c0ed96c1aceec6917b309ca4013d3a307225a62f9801010356daf0fcef2bcc6fa5f255a80823ff638ef1e33c90ff41fb7d94f3e0dc8ffedbdbf9160a624cdb0d3e687ef7d3f624b010b13f7ec5eb8a7f0d466c3f9671212e83585283e53a72fedde9c18e975d06812b16840319458f17b03aaddcbf2b3a2b862b78b4fb63b164b76e775ddaad2cad2f0d6b145ecb0295946fa1ab9a3e59cf0af0948b7a38bc6ddafe54da2b21ba3d1b83b7dbaf295eeef78024585cd349f4c30cd3ae71c700d3ee1adda893f627b8fd30145e438db1de5be1d56c31a29494343e6cd5062a552052cec5ab6f475cf38b5da1f8395e5fb0019d09f1f7cdc879f0f26f0fb259ccf109725d2098e232ac74e371350c842d169c2929bbc01ef25e6d604177d58179d1828212fd487e17a0f34f197e1c5b7ec514f0517ca09ecafc3d327995cf8a6e6c6b0d0cc8f61d0fde33a392a7825857663e9af2060704d23c96e06a40007580c4074d42bf4c7cd3fe6b47ead1b43e00c96d80db244f0568d2d084496a01606e63dc26fd5700270d4c98441300e236c46d92af023c696cc2a4f501406efdc1fdc6dc326360a73614b63256f2fb4560662ed11d98e209f05a12109733fb67d615c188981222d2f13ed16361da8a1cfa6b228b251648d6ca101f63f1534f8ce8b82bb90b17944452c69b26d228e351e84f12ab036e50858fb835b02585a07074921242448ca5afafd61094692321c47620b028fb4c4bc6c3c14dc4f8f34d2d6c72921191f0e4d50f06a7c5b54f490395b9de51d8dd580cf47e5d99aad0aaa1eef6fe3bd8c820de61dcddb2ffd7af13eb16476dfa7ffafeebfdab9ee4a8ec37cee412d9b24b3805e43b49397d888bc6b720a6a6f0ffd88abd732ba6955fb16addfebff9ffb7d204dc4bf088d029906f3aa930527b5d7ef8ff23fa9e63c2038b16df23f407f14d5dfee48abbf6a2aac5f609c042fa2a55459fd866f7fed27ba847e5ff2692901c000f6ca795d1a1fef718023f62b5ab9a5260089e0eebebf31640db2c74db60fdd3f6a003eaa4ece39773e7fd73ac8148434843d557839167d400064659ab8475724ba80df6c1ea5526c0f8c7f00b90fb7633c47c83c0f78b72d337711b128b830c005f02ad7b43979fdc0f09271f51c7cdb0379646a8d40b6d3430e49341f21c4c6693926f9b62853b4223ac8e7c41fef76ff6b3503a918a8e1d4abb2a958653bff6b2c2d672092ea18536469ef6d3e57edf131295fca0f91436c7281a45270f80486e57f04845d127e551c1b8ac593512e839ecf46cdb1130ac8444249a1a8b5f82662ab188b7374f56c1a244b306277945ceecc6904d53fade92e1b0d5cfe1e4ac78679df7615b108615831f81927c5d562655e059c82acaee7bab30c415a18aafd92fbbe50c50f0e9f3c2204ce23932a140303fe9b98df820a1cd0d72d19172f98b75e5b4c0816c46438ff47bbfe66d4474c8b39b1d48c3ed90ecb9376a31346ae2db642825c6c22358877b0c6fbd342d13f5afba358cea5f948d0a1b3ba191faa4a38221760e39db81d0fe69865a331dadcf0e5fc17dc9f422b358dd6521884aeb7d9ed04882ec38064d7755bd1c38bb8138c371d91848ef4793288aade2c20a5ca9e40dc9a66c6256fffc822cae9d457ec9e3b0f14b067e79f21a73f47281a705516670eed7a02c67c0639189b05ce4a41f30f81cc8b9a5331c74863d243437ae67dc544dd49af217faa3e72afbc70abf6fbc1cf0039797511eaa5880fb43d11a9744e99489081ce0d9c093b86f25e6af81936b10e06d0e787f1ace3ce39de9f994e8da9a64a857a642d53ee64d845d711b7b91c1425039018c6d34a645eb7128582d4b00795e4aa0328232dc4b890b62baa06eb6c7520525b564c4b1b26883d99c96fd9e7181bdca9595f795897e674116d06dd0f192c58605bc78c2d6eb48190ca5b0a2ff618b5d4dc53d0eb1e0fa253501b18ba96a71c1dd7e5ca9dfe4b26fbab91a1c8e0d590d993e415541ca88d4a5aacd40a5e5779fdf089341c29c293f1753085ae32c25864ea626bd2d9898946300938238b2364686837fb1d86afd031142ff02ee1171790fe0aadeb1a1de1c4e858a99090221346a464d262e5fc879656571b3c8160c2e7b75866856de1a8aed470b42d73bb5a523ad819d9c42e1b650af04cfd1540f043748a8551c4404a97179741ea69ca0687085b75784d8fefc7730ecbc8aacbedb170298e609c073133f6a99965187761da024c15136ff5ce9333d1c1980b9de86c0aff848bbbd0cf0be4f23e143898bf132364bbc8bc2b2e06fa4c5ebc150a8be4e21825f55a39aa75a0899aa0b18d29a65c459a5f2c9f5daabd4d8f55962dd748cc63a755c1bdaf1cfe9b73f78c383f12b95c0ea55b80b07b519a91ac8eae5509a161bc1e58e01de33d0aa07fc93a5f9e888491acae7a7535847d9338c9f9d73b94641767c0544751cc4b5309bef123231c7980a1fed97aa1577dd1036c5bf155aae5abb4114d7501c1282d6e02dabf0591571d2ee9381f368d8d507d7d0cdddf39560a076ec9a29b9eca7b6c1e7561a6d2e1720d0c5ea1cf8a1247a0deaf8290c491314a1877346b79fc7d406614381bcdfb9d213ce4bebd8fdb67f0a8c395fa94d1b846b43718f8d72704f329fd3fc3c29aa1ab6575ea74604e1962a833274f114255f1f330767763a24d2bd4f474f8fc28a4ae0f930f11c18cfb3dbc1baa0b98edea8172bb0492b818491670c027c724db122f0794c1aed0447a1242569514d62c97e2aa254581969793ccd15cf355331295502a084962a325eb873a7aeb183aeff52728d6f5c5f1460cb063f330c8ee400be583b273838cf689a2a35c4ba1e5b801d12d5bf0b1b8096eafac28046044913cfd8b3a555d7503489a2c9a4befc423b8c8891750d0d9ad97a6128ad0634ba584f8b9ab7469168ddfac1440d4a7429c12ed527986e19d8505f8a1229a1d9c38b93ae5eba2f5d4c8ca0d9ebf9d45dc711ac00b72732d963f035118a4b763fa6b5d71ebd613179a4b38b3ea912e1d034c10e318498719eaa7a856b788b3e3f0cd54dabb1e336aaa1d33877594511b01bbde2e0e3cddbe77adc8d0ea37534c48c978aa3b0aca7ad7bac0802969fdef492d2993e52c6b2781c17f7e416df242225783cc138dc2405723b7a0c311eef271c09178754f65d204e6dc0d8acbd84a8e79a80217704fb4d20804d375847fffaf5d6631800c7aa89c44f37a00a99ea7717bc168013c14b2176aa4136a4e66a1149d572f62fa0139ad9e669076c2740ae21d79c4418b0f001f8ea86f8b5c9081053e8eaea2109c87003391085632aac26ab0262d22eec16fb7e842e6b99bf1aa20a3ffa812eac83c4c3b647094403c889a40170a3f2d969580533a46f6d3be65d51557276fed2bb9816b389ea23941c2d7f7dd3b5136ff9bb870edb0b4cf5ef9f1ab239558bfff367c5fb13b6be4cd3423e678a8c749624042050fc1acc81adc81bc87baaaddc0655c6048475550a1666c9dc367cef64490803a9f14a32789f4f7b9c760fe6c264316b1ea9c7abe76f41912c1b78ae30f222fdb4a70a16371c55c478f457793958fa764343f989c56950140cedd5ba52a9f922b13dd00f65c35e748ca9f2d45d6b2ee586f7a0004660e2637047800b8802609f3298e64b93340fb824166d3bcbedc32d9e39d5f9540acddc63f8e913a8efa12cbaf73d799fc0ecc58ea0086c129bf1d0fcdc40b253e583c8d331ad91a3e76e22c986148af00f781da6e43978f69730e7c231d6bedf62e89ba02a9e4fffe3cb10e49b20ae90b7e1e522fa86e37ae11884b74f63d4837441bce87395e3a24e33d716463d0a7b427e31bc034d8c9c1dd745d6a83476293d75e0e0c852da4072011375a5145bb086fc611641ddd09c6171bad507b4b4b03a4325cf33c422d4634b7b54f68954d7977bfa7063b3ca986f2d0f3cd25ceb475667337a040ef0f28e368af152a4bb8e90f81ffd6263c38a4598cb0b507aaab4303af315d9370518471f2a580d6b8b825354eac3e987dd8a839a0665b2f9e0af2cce99b48c6eeb7e5c8c12e8d63216ee1a71927d6bc0894a376540ad6af332edd2d42873fee43474fcf25463f0e58035a9d5906ebc8716fbf0f654cb612d6b14c1a57a9205e2f36a69388346d436a210a085a539dd86c7cf7df01ac60fc671d2f42c1939b8478e061cf4e10389cbd6b69b0bf1923a99258588350768fa3750dfa275bea744d4a0ab838ad20c9ccaee28703532a4ddd1a1da1dc5b5dd21283e04c3b92a29ad1e13c6e11cae4a74dfd092031122105515f221d0ff9b09683adbead322fb952b998b1a31859b952b01588704482c00cf0aec196823384502207b4cad0e16b78202b06fc2a3e07c5ac003a7ba4833ad93e4476e1543330aac44a3c2b915698820744b13ecdc72285adb5e50ae0dcdb9ed5d7fa6ff5e9f56a103276249c5d1df3da05e3ca4990340424d9f02c8863dc0b6c51bd6fdf1288bb5f3ab5f010456f53921d917339dbf9a75436878b024780ee1d7f6029d608c8237064e0421906f6f0dae2cbcee954d87b90ebd2bcbcceb5a158274e9f198310351b346325f5754d938366e10d7c529fdc31bac94463a67837f4848bea691dcbae1741d24512a63694e5966197549c560ca4c8047e450a3d0c18c997ef8f1b24e6fc413a818f7ee503ab22f29bf2c538d3e119b06f387a01e1b1a8dd7f49677eaa6021109f8a819a82c18f18035c71bd11e448768710352f8e71a40694e511f595017248ea9798b710e2440b50a545701f1538114d440a776d82087bd8be7bb4b0059e6d80a26aea0f87a021304a660810565575e42d418bf4a33c96c05b0c26b597755b5e4615048a3c7aaf55cc0ea06a03b0dfdda89eeea019101e1e18ed5221b82409961afb3caacb0afd3fd8abe4fdb79118340e2a49ec6d7c6299a5cd2a90042223962f946c17502a3186034211753ac5a6b3c591f0375e4c6289ca0475f03ea2137064102beba97e013068470d044b9624a7dfc99268f963dc16084cc1dba29fedff270c983e3ea61d56b08d4bb80818e5110fe63aa9e9e4508828895ef82e88fbbc2be06abf8a6bd644664a8c730002ce357215cb67cb6a52aca5b65b2e8bd85dfab2c2e726f33863de87e7f59b7105baabd8a1c24f5154cad55fd4d743a653c3c2cd23d5a606f71e255bc95f6b7877d86e80435b5b0160c84fa05921b63e6f429e51dde17f08438496e4b6e3b558df03cb3abc880c09b26ffd0f5fb25f8b6ea63e74d43a64cf47ce5f6ff88c86561a413281dfa49b6f5786efdc4137adff645009f0e8ce1115a62484cee75e1381ae9bd4ccaadc4af5ee3283ae08a2d77d0d41c20cbf54c39ed56126dbafe25ac12f1f58842c2e15fc8f95269ccb8d0781a39fd0904624884f2535ccfa40e9c1b68ad82db0ce89d803280a0d14e6ab53216651da97ead19ca638b5b8ea89eefeaff2410fcb5766cc6a15aeea610f0695874e4078437012908cc293435f1a083ae00a94ed8a9b52a4219e45cbdfde0c33a4aa473ab85bb2b62bb84c1ccebf1acce3cce52f7ed67278a20bc75a0b0bb592d0c34dcb1f9d3c993dc5fcd8764df8423c7ba2059aae7283af23096e9cc04fbca78554eee731a095719aa74dfcf2be1a370a1d13c43ca8b47e721a3be5804f9fb6d7d114dc7824fa716c8bbf0976409590f0895b46d1e8e7af0884cfbf796b96faf3fb346bf02e10de3d53b5115d996b4d5ed2f54319ae4fa5e74ae174df351bed79396faa8f4f4cbad9f58aed7cb778298de1cda7aeb50d6477e935f00b13e20b2336dffe841f6021b531d3524e41c14d783f48c2df0b14922e6d4fffd354604493a821394d00e86f9d80587508f688c562d7f58bc3f0e7a94605c49568a4f4219ffdaebe979f326fde43236eb2a83629e0be94850288f61ea450f5127a2bef8800b993f265c47d7de9ae0cad1c6afa0415987235b4be9b5d76a461d41ea9100e2ee7227e0363db7fce78d4a3a0071700e9063178e0d5c1eefde7c360da54c8d4d6f000d090c4839d09378b390f6000cf74e75750ddf0ea731c643ebdbba43335cf3098738be733f60aa318916d3bce45bc2d376f774e038dde24910887e20b6fb1b74b3cde9d6ab136560922712d80c0f98fef2cfc5839aa8a3d1e84f88a7dc2ebda35c578147d387a92d086eeb9142905b5040460591260c6b857e8adf496811a3e7e536f88151b35ef6821c0023cc19abca67d9d8d1544533aaaa3a5c5033002773ca2c3193bb3d6e973b2aff7843d0339f07fa54adbd778bc20f36d34578398397a3e2363cf8271b16dd2ca7eb3171ee86b25f2dd6fcc5abcec0ec9717d701f65fe745c0bb4f64b1030469ac6f64c81302705667912b2d9c437e4e069fe75f98015818f8b9c9c6f4812cdd46576a57b5b57ac0fb2698f0a7a77c311f1a71ac298fa2ad0454da1f0a249431e78e72944a105304b4fe9e335a55ff3400b7e23805fb0e44d1b1fe91cebc046ebe5643d57b925ebd85b0c7c0eca15f22e670417e1ff3dcc7241b118a090d4c241f1f50120e34edaca5e3b617f683725ffe13fe478ef4d9ad73ae6db21cf118c18fe8414f0c1f379828e8b78a645117c6899c84d7bd982a358c21211c62f7282d5caed3f4872428d82ffa3529526fc89748335a2e1bb7a76d3c3180259acd7b1863d6a7e2770860b240e546b3a7480c283ba78976ffeab77d05e14650cb220f6c4e956d7c6d162bdf5526c4a28b87bd5fe7e1b10d85142f6575e62ba22d34b1486466e9f3829393e558f627a8fa6c60955e2e503273cc813fae0ea396aedbf951d44664267ec36598cb01e508c265df7654acf37f863485937a2d7962a7af4e3513c969136172af3fa59e9810e9ee1ac1eee943d401b56c2b8aba14bbafa46365cc7fa0b35b89c35b98f0b397a9317c8630297323393c5f93e4286e3a3f4b5b3a3c9e63a47de356dc1d10460ea73e206019101b318a8ae303a8f77fb5a9a5c165be836a950521c268a00e17f619afdf686a2823dfb8db09556bf1ac6ff6094ae316ce38524dd163a2ec9aeb6e7ca63c8539f8845efbfda0bda8985db14e3f478b52b4db7151fe97f73a556cddb8ee20fab8a8a8a3292a69e2ad42d74eae146b6a152de8bc14c6e0e90766a0a74e896439fd1ee98d9f6e64609df8fe10b0c544c2b6ae5e995af0e787a96ab4cbc83fe0012dff622fb3647a804a196242e7d50b208241c9b6853896d84815164e53b5cc327054ec89c5805503575f6d47e715775e6db378c985c74ed200d56afe5f56441f3726e48ec32e83a2849170c067606534c26698b6262d61ce11bbc86444c8f1a823373d41bf1dcf7c8391e5bbe85d73437f94a30174438848c571b6c220cb64173dbd402111f7957b88ca0a9488ecb503ae5b3e7226e11cd2529e4fb7d8b5010649e7c40e316f6428024090749cf628aa9251a9cb5a54f70475caa2fc4c1bb19eca3c0df2f2c99069d0c5874a1457d84c1ed6d38a0a821fb624313492fed34b4abac349b2b76385f4ae90da920474dacd5c84af314be5ad22b798acd9a89517fffd1b1209b4523235cd2488142ad55d5a91aee0eae5633d8420dc04a96907377de3039416441d8d007d23986455a3b1609974d9e19ab15b6c37583416831b8f7cbb5c45ec289c4a7835d0c166b22a8af823bd1706e8245196a2a7d347be0407844c58d5332886dee8bb081e2a3d13af95f677630fdd85f1befbf0ecc628fec511e36839695ecf91e46f9615a60f51aa89f968c1a4fdf5eb7483cc6dd737f0d01d4535d377ee864264351eae7eaba9a9734d1d089f108a700d3053aab0c69f3072aeb0ccf3c5ebfe6630c9214c2d6d0f866b0267ea5e178a166657701ec9658871c64d50684fd9af0f48d3f49416e3a5023b9b1ad8028ea2ef6f029f243b0540ec96285c976f90886e9c1d84926d4a3c3085921ebda8fae1704a94705bef3d67ee00bf8754b6a20c87000737d7e9b8505e9da5d34b0a9f41798c408e656fa01fb4d4c2709a3dea9b5c6cb71d648ee64e4fbbdf21728d78bd5f6e2698ade5fb1a889588394372792a238b789a39f50fc04a5bd6f3f8f0d974d94a4c7b2e988839032b9c4e97c99471415c3bcc02aad3b5baf0e7a34135f5be04fe4f739fcb0e2f9f261e28e745088526f554b9342d15f793deae04f41476b19e28e3faae5a81b6418a79a4400bfe3f5b1452e9b491575209586b71ec9430d588bfad0cf46666cecfb69d8b5c749ea5e61bdc3abe2e54f8008610428cb5f086f49b0bfabd9700df6afc5b951afbe2ea654378be33555acff6233d99023f0addf7117589a356649c91181fcf868ab58f94da7f5b5e5ae4fa32164e4c5faaf4b4d0539ec9c1a5190f08357587e040ac7081b018b066866e07cdb7a3ffa84aacc28031ba3026f645f0c162bed82f28a5e95e46106c5c308300d337dc30214684bfc1168d89e4488b89daffff624dc0d6d33d7f68f63f9dd110bd91fb102b3b40c14d7fc6527f358d82af428b119cfad20600fa61aaf6ec75b5f9969b79a6747b84805f949fb3171135c93ed5f10e3018dee74c0176c74464ca1c0c42a19290940b3dce0d02e4e7f8fcf29b61903bf0ea867d7890f7cd214503501605d451a89a45221ddaa5c239f762438d7973f50a9921a685bc485f0a94afa7cfaec8419b78ac695032ec99d36c414cc9cdaa1063763b79165faa0a160bfce8b0c0671111837140f4782f0710baeba1c7be2a7a860a8265a0a349e22b6fde11e7c0010180f263d7580119e7994e9dbb710853ed40b8478303fd37e7a8ebed6049b04ce7029ade2019e7428b80d9c5780f1d3de72510ffbc8c991f940b9f1c489a7c1f3b9642c431d25611d08d91a9ad086573aba1df7af3dcfa1dfb82831992c43b9bc111988a85ce0ac7b3d9f2ea9df6cb7bdebc6dcbcee3c19dee3abe1a13ba2be7af690b898d20aacab840f003ee5f2b491d3853d983925e54cd9fe1c06f15be1c56c9f954a6fc058a53bd27b6fd185afd06e91d91bc8271290daf34d69ccca57cca15e3117b8c94f9b74b31e80b7149c8350b5a77413d0cd26e580712af40a276788c1f37ce3e7e2796621b7d450ec48836dad0d2f7c857332598a22431c868048c8b75e621b8a418c0fa2aadaa42223cc7c66052f9cbea1964e297af59b550a79b7d7f4a22744aa351147b780a6596f2f8cb5d3137f3585227ad8a144ed9c9c71a4889356e1b5dfbe46a9f0eaabeb94c6ac46ae6e8ff421c838a51510117cd68fc0cec691fca7216cc06cdc259d3e2c07a2000308204c4d590ab208136fb46e0da98c8a12f94ab2111771247e390dc74c91c05960afd03bd9fb41dc5876abf3e75cf3e5ddb70c82604f3b068f0818aed2cd7220415e3f7b15ad6d67e95c0073d1ee4040da472e1eac4a61a00394deead1ba8f47aab10cccea2b5d67a70ded1d76ddb6d0be1703f1da34356835ca9fef78e7ee65f92a052f95c5337d62163a47e53bb303c14d96d558143695fb791b226a6c72732d3095fe2ea3f48eae55681bc94f524cf07cc1cabe472ec016d9b3f35102d45298e1156885fecc9804c477cef03b59faf5f386e93b4c93ffe454874521ff50820e8669f286842212f7a494c615bccbe872863bc7c73ada7ce363efb0d6cdbcfc493fb3764fadbfd6c7e2c8978bb62982d53ee5618b045cbf934d50d4f4268e77033aa0872c5aba48da82362b975763a5c3bebbacffcf095f5cd127531bd9badfea31b195bffa9e6edf83846c58a03af2b2dac296203f4d5dce01679f05d3d768709d308735bb2806ea297f6817b304c42fa08e633c4dae9be9daf7b9764fa7e72f2f7a2e5fc2417d756df8853673064f5a911947bbd52e2ef35ed3821532516e97f9191b41546a24b09206e7fb404694b0b50d6458a7f0e55a907b3b36533241612a436832560f258da72e8bd09682da2da4d8e177fdebb4c6707ecd31fb947300310a00cf9e7bd582e430c675500611f0db90c21d4599c845275853502fe710b2b11fa585de6162acb22c1c5432915776f9b13535cd1c0d674b403ad82685256607cf0f15999d0c35f0acca9bbfe4f3133f92063a73fe1691f044b778f69d5b1babed7f154e30fa17d84c4984acb0d723e2e959efee81174f970895edc9902a68d3a721770c14582a0cb6e03a45ddd62511e8da389bca74f630b1eba9645b6cfa0a018b41718059e4970a208f6dfb6c9d26061fdfabb3572d553583131d150e78b7e9864cda3247a5eaa677ab3e8496f3859830391ed7fe6eba169d7d7651c6606ab96dd7c43c4ae02fdebb116309bb8380e6f625bac0867bdb27b2e9553cdabb1cff932c3e3beb0bf50e2029e9e13779b0bc9279e19e4549a78981b1c53d83b01d9cc2f69fb485dfe81c9e803d5bf9ba2af1a16a76496e7ece280322f23e9d24c85d523a09abe2585c3af71ce4cdfe1fc8d7586edb2fa09dd65d791a861b57c243db71fdc7765c047472abdba467b0cedb43bc12db716deb8ebada2e5241dc604ef385b314e75bf6e6b00fe249fa3025aeb27f613b697005baa733eaf3bae1d0e883ab5345201a4a7fb39d61e29bb05a25a542ab1b496cea004e13b01a2583295680e47a12cdbafee4dd14d7b2d2700cea0877201f7f1af56195744e591db3a9a92a46282b60dd6c63d28b8c0335c24b5357f2958248c08dd8208ca90decbb5110ddddababd7e6bb6706e4788448166ff526986856c010d1fa5ded3066db77621c65ed524adc0ed220b196d10cc154537ecd4fc29bb2f1aaaabb3388c7590711b11002f2b1a7656c5505d0e630c5a1282434774c1536395dd85769fdcdba95c7013f03de5ff492807a509d1b55e319ef491537c0dcf663f9087f185c48b210d4e9d71eef6f1ce1721d60895d1129569dc98b8cae696e0472e622babdc0ae56609975be1c193c963724ba6d30dd9e95b849d5b75beb60bc392b1d0d2d9a8d8abf044c631b19d1610af157a57a69ea213d55b75122e48cc01aa3dd306b86bc5f2e94d7554b4a85c75de1bcfe5dc2088d5c7c3b3063501eafbebd08a6374e72546d6310da8a9ce7bb9f117125b4e19844cbe5b59d94145c278a20c624fd8c075dd1c2d61ff155776a8c4472094993a113eb9ccdd9a8406ac8764671b4090de4cf2304682497666b6f5bc737f868ede6cef0b8ac7ce92367e9e786a4f17e539b7b04f0a7bc9c4dcc5f36d80754851a4263ad141ee1fdd7255cba9242bf3e115f0944a7d44b49b3a911e9c4221cf637dc26267353d5cfdb0a63fe7be6bb0903eccaa0aa3ff71839cb511c7dc3ebd914635670a1aa98091da45edf3041224d4a12c58e7e8d6ab6d2a352cf5751883a3f473515f52d836499d10da69df65cb1e84d42daa21b9c869827d58a0f5569c5d91f57beb05901dd420f068cd5467e137d7093141e643d3b169e657b96e7147e83033acc628bbaff808a7dd27cb72e7e02c6cda439cfaa30a213483f41b311f14ee9ba43d762de3460fdd13b1a21eae8e899411bde23274d122cb26feb3217a634ed5d209c61b7a904381e3f28e438932c2dd1922b7519bb44c6d682d13de96a98ca699f3b500df5f5a0d5e8c85a754a448febf163b7ed2cb7ee2c1a6bd846d9b36438aff334d456c5ada4b4c36422cd4d356aae3e7d87a0b509ec3b141bbcb4ebc36e427c7452fac0fbf9b76ea725a4872123aa305bf04b69580ad59c3f252228bcc78d6961481625ed2187bec51a4e18682faf7a97019829f9a2064b392b5c34cb0e142b42949fa2b4e9b108b8adff50e995e202aab3fc16d0eca28ee515529f47599ee5189efb86958a679e198423e577ba58050e4308f98bb4fdbb95c81b536f235755adb495d75d11b17e2e838f2b94516ec1060f1acb507bed965c8549657d127b16974bebe94d67a57a662daf3e1849b51813032f532afbc05289d78fd91cab41deb2f19826651cd64a5c103febfcb2562354f5be37e945e6e55f00ec30f2b525b260c3596653b3d700069bca55bec99367c2eca8de07bc48e240e198748f27e490e13f4041b118bbba3fd5d211afe3975d0672dad75b94458ad1d4c27c541da83fa4af14e07862820e513bcc9e3b5a7776953408a4f7b7ebd639dac2a8ffc5027ef4765a94e34a600625063ddad708c173fbc115f038d24667f919bd39cd64c81383776e23e6c4e8ecd77db29f7dbe6b0cd0988e623c4c551a3379d455fd24fc2e5405d4221d9b3ecbb722158ae8b97ed358cbee622e6d7dd9072a116d437bd974ba00332c22c8442d4bd8b5c6d8548a4742e0d545578eb642cf4292e0533c999cdf2f3f2ea7906039506ff7e214951f2a13122f936dc4b4999979a311e85c60e3c1ae3e6c808455e02672171b5506c535aa23aa198fe75a9227cd31e1e9d28c4336522333fdb6066d67ec448b05966c8fa3bde435a5f0076b09b2771eaec73c02505f9ed1e8ae0b2708d4b8dcdd8d46b04f2d61c5820644c3adca7008e19f2d241ac9320b33a39fd3668052f969c38f3cc480ab2a76e7f915ebbb3918b3bf29650067d550c802e7e288ef3d6e5155a05eb158bad319daa95573408075617023214127edef01f3888469bad81cd2a63fbdf59376e0874c504ca79223ab1e2c7342989d6080feb5494662b8441781acdf366ce1034807f7a256910536047552428a6d647b106311b0bc25d4463ee202101bfa2c60ee8cfe219714c115413020581f9ea8cf39e0a66891222a178a1214b88748c01bb6bd115fbfaddbe58acc60346c94df6f8e42c9cd775468524c4a79ceeab80323f4305282426a19e60865b3b7c864b3e23d19a022fd444150095d3d6ccb102a15539977d9e14a10bed2ef393c734c4724c51ad9cc688c082fb9f6bc4faaf00a5359bd8e390ce68cd4969e58dbbcbf0898da896ed85627cbed4698bf1e9fd084f5d5e164b2a33972e9bf2282f908ce291809de7b81bef360d33fbab6e60610402f20b90fcc2e248e7a169d94b6f54648318bc5a83193cc0b212dd5b740423a723dde5cc98d640058dc0303771e45e302230fe1228d84404d391e9ac01bf7ca924f525516c24195f8b50dca33d25d93fbbc152a539c4597383dd4afddea862341b9c12202e3429c45fa8b1f71baf0daac735d443e6c5c465432c90ac52a507d92a102605cde6d114d5d48d1b6750432f64df3729457a9d11c6fdb685d2b0d0660a6de2c5ee79e223aa2f3fbe08d9e4c7481ff50943e740ef00bdbba3d33dbb133544082e4843bc3ccd2735a751b027ed09d68df3dabdeb3e53e07d6cc6f357fcf6343aa0ab42458752d615c8be990c1fdf5d905c65268541cb402ffac0a0109152ee4914052a24319d3a5e411bd83ca5b63ce7df63693fde13e1e9ded4d4fb5d1d1cd7be47de9b00ec66c47b12bdfaae74bcb371037ccebe5fed50ff4cbd8abb8d3b12816252712794b8c936ee3899bf634529d3c85e6fd4dee2b61ddddf4732007bf66d8e99388ccaeba535489edfced10df4c664afa45f68af14f07d7531d1b035454e26c44d8358aea5a8a94142bfc8deec2f2f0dd38a520be6313dde5dc8f2746da2055ee25794684deb1c709bfe23c85b2b6795233115bce88409d08d2720026c1f74df0b8d4f2b67e5559d0b2dbfe4add101255ae70a3880357687723e8ec968d1316fe8adb3bd53073844cb5dbca01d7f2e7057ea560a8882be7a903b9bce952400e38c95b43bd05d3edc38dd4240adf07cf662f936e0111953d228ef7abe884dce19d6dbdca93acb686bd38f3c14a7515ae51eace8dd5f87b70930bbe3f9885e8b0b22fb58366549929db0ef224a42592e73ff4a6c0b8fc2462bbb8b26d9e79515f5a7912365fed14460af02143576c3f20436d111f94cd4490c38a1597e71ea904312cc5cdd8b00bf9355607d176645005d2276bd31325dc759e75e7557139a38efc16fa343f8c1971fc8ed11a406e57f5ea445b70ae30aaf7409bfaead8fe841f8cf32bebbe857f4134f1a4d708bf7dae09ffb277db96cc4ac0b91cf73576ac277da44ded7ec03ae3e961fd4042cb5e8e5e0c269c6452671999979fea64af07f0df94b2c25e759e2e041999a35b451fa96ea9d419e96d51a99e56bddef5feac3714ba875d294cd59f13437613ff082a5fa01f9e41a60dc4a30bf74023967729b62d28a779174930ad0329db9a38cace7d49a7b6111604d9e6690f4f0286239742fef1ea1142de963ea430d4a1756d40120f2340bf2b9653bcbc0d9aa4cbd23f129b02c83a4024c3745ccb2090d3a25ab09640b931612b585df0cd7ee700031b65e3aa64143e70cd52a9d13a0aed541e4bb6b57bd72f0806204870b8d03a6572e48e1f5696e7cfbb3df7dd63e20b925e0b2fc33c477bc8ca798c18ae4cd2051bee08171067429cfba8d530fcb33bfc8c0ef754fac436197d1ebc6026b15e3240f3b4b8887826c0ca66d13b0f4d81b4c6359ddf5a3ab2c6019acf26b4326c942dbadb68cae526668b89aa2fe585fd888454aa5fe2f0a12e6a061c39ddd1d7862c3ced495add09b23c107866cd8769939ff946b2e5a9ddb94cbee7142195e0c0a6aeade495ba06de65c5a03d445e0f744151e2e8eb2f7380fdf955984e37ee7ef24a6d918bc7ff970adf6aa091a1a673300f00b4233221f4691dd33d8039d591096453846cd008ddde6138e27585f588adbc44d7456f6613da83a8425a42c1d6a22d9e09946264b839b62a4a33bff55b19701f462aa831a443e0551e674fa401790bdce07f33d92e2610d3b21c7a27f02765f4d25d706356034c138cb5ae2a4bcf43bca0cdd0594689512fcbfd0313be47275bea20f2442c5823728db6ed37cba2284765bc00175e3dcc09ace8ab8ba957a299b48651dee52e07f4e3b779a26c952cefc329f638e48c5221f0e256e0412aca0e3e8ffa26242b8125ae1a7f1e99e79f4a5e71edf8c4a39a860bedb2076ce2ca0c355504a66066903a1fe3229a3f1406f993768bf3dc170d2c50b2c357bf9b69c6a089a1179ae054b3d4169224df9a1f90410e05ad6b18a68f966445e52f361de431782a909e8453d79c41c5506d41afbce53eaacc57af55f4d71db2ea2a0b9260af5802925e52b364dfa60c055c4a2b4f457f33d03a923d5dba1037cc3d2fce765471183971dc764b7999f43ff47b1bba9881b520c91c9c455b379c2f88e9896ab8ee6ba07aa8668263d73d20ad08fe61e6d25946162c3e8a4de9c096b618e91463f4586fed87f0183222b07434f86819bb6efd90cb3ee19e5b85062638f796139b2a8572b598b026e0132104947a253125fe8d56c5c342717cce4fdadf2c28034fb2715aa3ead5f043abf5f7a5538c50a08dc0310844fe61488cb3768ae84f2dcb25e1ab2102ad9b97cf80a6ce4ec4ee6fcf5c2b9f1117024d2a51e44364842c3aba309fc317e6cd899453eaf6036fcb8d52a7006209a81fdd103ccb530e8f601990a57d34719ea49e6bb0623456d004b8cb65ebd9018a002ae8f6c2a5f1242cfa3bf5fb2f40976cbc23dfe5d72e38650b2235256c8b41bec418a650e268ba85d2506b8d8b02b20d7a6b96c4e64d6890f6f54413b6cedc90ace3392bc9a0c239068b72fe8bd43d8966198465c0cd44a50c11a1abe1bcc022b3050a3f1261c2b5e9d8dff1b32ebe550e188a54971ce80a31e2a059775f046878b12c127e897320652662224f15cfaa0eeaa21b1f41a51c8b551d381a5a5db9a84b462c6ad52129649d24182a7a49b8e4258acd6b5a8eb4b35664a6ce3ddc8c4e0a1ed0116a657e6595c1952560342ae93823e9472e894325c6ce9bf9b2fd843293f3b1168f8ab84fb9dcdbe3355706016c690a8a4eee2611a4d8c693ba3a637f6a468c938f0a973ef40ec63766cb594801bd0d50be0001bd06657536ed768d5b75c5f8bdd72928ee5cf7bbdef1e6a29cb1a127bf988263a9b59f9e437ec2673c7719e2601d8f5bfd495f711e07e8b9394b6dcc4d4f54284bde98b88057ea1523674eb153786b9ed87873c22c0f29e7cbd9ac73b5b09e7799b63fb161dd9fa84452cf51b6fb66bfb06615162915ef2578d7e065218d62eb5232960bbe76dfae324e1ecc6f47b2945471aa7ed994286d7a471e454f1d32c91f85740135a58a836570ff3af015ba6902d4b4a36e2a3112394cc3e61e55b4b61063a2b41175c3d290654300330faea4153a8e80e15ea4c19c90e65f253647d533c11a44f4c5a9cfa793a67673b4819ade1625ea71de20e689d2e7124da1ea93e5395970ec7bcd9b988f5321850fe6aca923570913b28ded5a855ed0e7e3d990a3b8902167db53fec974e101b410da434d2f41d22da370138545055a2e26357727bc5579c000a87c081d2190181883707209d95c683e633bff77287a8b9014ffca754698a3346c93581a5ddcf1202096a199c0a85d0055536fa1385238a7e844c2bdc19c130fbd45001c5d42fdd1a0b874541e3bceba6de69be3363d870f35aa368e484710f02cd8da644130f2bde7b16678384fc3ef5adcbd0477f18a2e738013b30f1465f2e4e4d6000bab2bb08b0ae8ce2959376a9ca057634823b359501a70e7d526906b4f110e6ef829c12c4f80aabb685f42aa375e740fc2dda7f20a6d8729392e5402cc447e04ad2970becd5889cf7c50893277b9eab4798b00e7aa4489883e39dbc2e8c77dcb993f796304ff402eea52bdcc6a2d0f87c3b89d9d39b1533cfa96932336e2bb2b4cc04aaf80bc65a30d659493afd98d7fc410f1e657a4793aef8277e342675074e40715bfd247a47d59371dc43573f2115df0cacf3a371fe95c0d35bc8076b15e9575c4115e12fce2c39f537baa2b0d7842e5b11531b1b698d1042c8de7befbdf70e080c9b0c380c34f213f31004d7c1a121f978df030f3f7c66bf6dbbb61d74e0b9de52c40e4ee083d337fbd874ececb0818b350c3ae30647d2cc9bd5ea1a71d864c25d1670ddbbe2eb9575641e54311b657f5d67a4997372b37b4b1c9c135cb56238f33d1c08c7958166c79947231ec3a9b882c2c577b3076b02ae7b6148e9661034b2c3861d9f87a56b0f40cf6276f685675ffc4080a484ae35df6ba916422631a57816496bad9512be2bddf9acdade7b09f5bd1d91ae32da4b4c5fab9552decad367999452c6ffd88e3fb0e7e7b3407a24f07dcaf7241676d82270fa3d0929a51a7cd35a58a9acb5d669f18c997d66f17d71f568c0997ebfb6a6eea4f4bdc22bdf9c300842bb67fefe611d4e5fd705d4570f353de30acb37aeb0dcd920b47bec2bf689e5f82cbe589365b750b87b6f76d15fbff3165314642f336120d53d4f755595aff2f7f41366d5f51ea9b2b7393b7c36ff8adf2f2c0f2fe1b57649e7f3aa40265c64efd953bdf7ccebd6877b557d0fbd7ccd9a0ce4697c59e6b7c42eb14b9854f122e7d178284a29a743d3791b030ece34a5746a97d239e79c45707006ac01676ce05c21b20e6620f311de47a885de7befe59a199c0232af56827bb661ecb1f2c199b4da1b57ef10e6f8856697a0a6ee28adb1259839fd673c1af05688f8285eeb05af95da6157c0755a471f1c3a4b7193dacc28562faba1a24d07218419a5c5d66677425ab563ded444b3dfb3d941deba80db8e4842c9d822ae9359657f912822c98937ae68ee525d2afe9d4ad330dcfde82cfb2afbbc37d250abcade033519702a1d78ec30d35fa99475c2752854b4e91e10e22a36c175ffdf748aec457466c2b2bad23d2a1d73e7b302924f7352d4d6dabe525a2b897455d21d109494164521a00c314fed469865efdd3b93c415a552662de21b73fc8bf9515cc6c23a0340a79c31577125ad10d1ea303384cf4ccf57b8c39bcc9a194fcad24cd6834d0234515c26eda1b5d64a29df636683f7bc7ce3e744cd39e7ccb21d6c130e7e02d1a632a928d9fcad99529a0226848031de7b2fd0b4425cb1281e218a48909268c5a2586485a0c24ce99d86bad1dea5b6d7d2865fa4b19acf0c16e50f19cc2586e92eea9dd6956b7987eadac9d46ea6575d6770ef6abad6c06ae4cd4ad2c8d7d4e00b85bbdf116db6c7dbd0ddd40b4cee9e95178cc23c2ed8470bd6618747439ec337e24ade8473e24abe8437ac61edc2ae7d527c705e2035925de2ec1797eef2ed46a8cb9f2e2f2ff3866dd48834f142e17018b5fc74a2d873e48d8b7d47db29367510dadd0e5de2a691cb0c0d5539ce25673912ab5c9c557858bec95e6d2e14cefe7aa75d289cbcc9e22fc7a55256d64c9c96d78c41c8de4c07df93adbe9f370c7b04677fefe7c3d258fabc564308a51c413ac28b222445488a901421b98a905cd715898e146d9bed7154148f6ebc41dd7e461bedf63677409089ae3577401209d111222445910849d14574840849513c42725d441712ecf4461b0e2eddd5228dfd09c36cd4728a8d5a8ec25ca4a1d7b0c472c5f32d24249420211106121256201d8105d20caaa045cc2552494c722488b8b28f44d1c61e298a2b9bbd8b45da69968132ea3577a56f7ff5a77b7a3deabe723fe117694c77c12fd2702dd9943ba0d9d7a99d6db56f36e667738f368166679f401248367c427b3e2281345cb927b334db19445b2450868ac9e9936afb6ae38f9e3cf43cec423793eaee0706523d734fcd51b968638fb56033d1a6e6b85e8f61788bb98a4ff84ac8152129425284a408491192523c29913d2b0417a58c55082e6a93764048d92b4d944685ba392fbea7380716b5f5520d85ea22dca5a5909c447afe6d393c2e58d6c8ca4aa4e5e9b479a866ecd2b5dea5333864838010ab3e5ec91ed6dd137a8fc7e3a26bc53c6428c20db2f4fb7cd75289f50f8ab07e3332297ba5ec95baad610989176f8c6037e2441ebbf03bda6cc05e95a11ef2b12203844ad0b747cdbbd2809cbe971b8f686afb0cd25247a428eec8e9fbb8e9c040ff10d1d1ba181820f482be5b0ea125fdb3038caf54ea882ce97a7b35ece64bc3f063e96091481b55d1f5f8327717c88babda63fec8ee91a7cf40aa81525df3213cfcc42a1edd063abe464a85ccfec0c519f4439281928e30e88704032df4aa1f120c8eb03f709209fd905cd06a98d40f4909183da31f9212253f3a3c553ce94e4717863ad09d0f467802e482ee8086867cb2e80ea868e8a73b20264540ba03da22a9df7f7438d05d064c70d4ef3f3ab02b2ee842bfebc07c50a57f5e962c55fa5d07a7c8957e2f4bbf37e422fba1b7d807d8f6b0271e1e8905617404021f0dfab2743c86c5fcde0c0aed8fcc70578b30bf2939d2dbb16714c3a60ef6edf7da21d68ee1ed71e3711dfe6578ec1aee6207b90d5bc8c2f884cc562de288b9130a72c263edb3a892c0f57a3f33cf35270e203cf53707d5e3f0582949239f13a7b453ae7ae08e13ebe48e1f4026abac31c652bc8c528a2c9394527ae1202754d2a0075021d84352aa83598aa34e064df415870f3880a086ecb21b0efaabd2e9845ed0f175061d339077bd50f6b24b1be2249e08f60b737251fb01ee21ada08b96391e80fd82c0635dd949dc7000410d2c2c675b8f9d7b10d4c0ba7eb1e2c60373229fc54de75431d7f142f03e59ea20d0efd1d0e273e20d3a7437819687923deb010e831fc28b8e3cdb104b3a0058ac5cd8af5c0309709f13c3e0b05fd8eb151c96553a7168a8e5890c0df5c583fecadd4b0799ed733d3b6da0cea6a49d85125e4edc4a275396994ea793a9e519d7722a95b892a92405945d9d90bd992c76d1b5934c49a2a4931c919ac65d155f787681b3af97af8132dccf88abfb558d48737fffe2fdddc2aa46b582a3bfef6ab41260fdcca22594b2b14b55675815afd20e555a3622aa81b5fd212549423fa42458f485e58e485fcf6f86ebf7f79cbc81acd2ef0f69e8b0abb26b604a6ba0075dc2aaec9015591c5665d901973b1090c5fd19d44a3ec0363a1dc39d067cd0d8ecb7e1d8313d27968a4016975558fe31656923a21aaced454c5d60010159db8d5c532e62b364c982c3cb8265a8c8cb826593fde0a58087db0ddc0db2ed6fee7947345223a8004be77e482ab04205ad8ea11f920a8a5041108dfb21093185ce4c6ff64c4ffc94e1116d728f7c3c47d41377553ee69ef8f9b9dd106573d146e69efad714294908ba53328f1db7792edacc6d076c7948436c79434d10603495423f245d74fe813d61ee8a8e347c7cc72a1e540a0d2f714f7ce78350c3cf7e7135347190979e71853b4aa1210ef2d2b863eee23b1d78f91cf032773002991d1fb38a07cc4059bf03655d02230d14816495ce7c42837e3ff56b234890a4e5af7ad4f4d72311d4aeec70eb217764806b08c4b5bd6abae6aa178c5b8febd7150fafeb089696df7e1e76285f2cfdc02c70fbfdf69844e1a545b8915e7aaa699aec4113b518c7106f9b8a045154df48c0a4e5e5a6bd4dd3ae65cf30de345cd2b2ec0518ae637a0106d3f69ca82add4df57b95ea7279215af1824aa552a9f00b5c7de1307fe185d367a81854128b2a866c73d166b4b97a64fc7a4d77322ee3d7e5af960b757ce1f685ed31aa2676c3916932cf892f00d1c9607866bace76ed190cf99972a76d4a54aae744ec07b6ea59ec7aade949f5b9f5a0695556f1486d5b0e780dc6c01d6c197f5b0f9ade8e63c448c5609a24aee861b00423ae4eb80bea162c4125dc0535a449a20d94589a1e4a30a215bd5df57047b54db26dfbc6f343553f1c742106249042022a47ac1ca172444aa6696fd3b463edbf7e3d075dd3b626dbf69ca869d77de9f7a5d728a24de9f459a954ba4adaf59cc85d1888ce75edd234ecdaafeb7a107754c96bae2f13fe815aa9b41d7ecbb0495cd1ff58c89f8f0f877e8c3baa04620d5ec74eea35086b06824dd330d728b61baebe72a781be9ebdb880a167b71e0f2182ff0fb19e8d0071d673e4f163bdbea34ab42c77324b962c551ea34a9adeb0eafb9c88b594af517454494c8a2b4ae193a67f4221134db9bfc917f0d3b9f1e0ff72e3f983f29ff1eb912ad214c95e2d02136c494f193649cbcbf8a6ea9a7e9bea393116a0df26e308aebea6bbd8dce1d643c6bbd85ae40eb1963ba198a362a18658f50d77321ebf5d5a893631c410430cefa2941cab8ee16fe389e199d46288e139d1648201c300c36180e171c301c3b6655936022299c3ec85928c7e38e082179dc9d06270d751f3136a88bba00e7ebbf64d7ad105f5bc56e91119d364cabe75a6d2e0062fb335803680c2bc83be14471d1383613e711ceab7edc0e494a48126219798d22283cf1d949fd126defce62c1bde0c72ca944721ba1fd294a1fec4a83090788005c6c7df292590d84f4e09043650c4112876e45143c753498411a6119e4d0c3f2f4fdf93e59b4148c248b4032f6e0a3a7e07431d8f9a53ab4770f4dc9c6fd5d157baf1903c3a407b760fbd94a73cb0d2f442ad33e9a437ac36cb052e76b75ac5f89720b1ed658e99b91bc49c68b5c07528d8a9791bfec64320d8134a81518a122922905284889291f2830b0bef08490a137dfb2149691293ae3067c2989973c61ca8e04e2acc9c83125c9730b440a7d441113de714b07e485550014a19a90e82d0810776ecf8d8a10a29b43c0f3f2615e6cc8c1a36aa8042cbe7dca0421556320e15b8ae06cdea1d21552184b6fd90aae841772be9832b57eec5c18203274f15495a45f42024095574ed7933593af08093b363e706b3e57de8c0034c42154f48a42aa498a92f383804a5af7e485530e9eed2276c142e0e169c2bd98b3638e26fc7f70c7e6218f1edb75dfaa88ad1e6fa0d763aa326de6837db4de9f43be20d57c21bd6f0bb34f1662eaeaa6c05d1280d8949d37332f2497104d98baf5bcd558d8c5db4567360c346b4b971f58e8e9c102549c78e4dd4af1f15585a867e4854eca0a91c49e9f85a371ef13576ed6057999f23674b492fed1fcdb0c6ff98babeb35358d1416657d9f116cbcee0466f71ec9adfa3015e79d2705e12e1ddfaae74ad38f539f53ba24dd6f53b51743d4fb4b9bade47155daf43bc89c1aae7a1d66a82ae50b4a06b9d41d75ae99411a61fd2145374b74ae24206292663ada852a678d2f7de9a712e7ce51af7fe462ea68ae87bff3783cd5cd448e24afd90a6106246a49932e64c2182a6fd90a6f86166d962ce9930662096380499f4005f952b54dedb49d5c89b0380200649ac776845260979531022932313e3155bd82b20931e8440a22b74844a62db8c93b3030a19ac51c346b4b95c960a5335ec10388834851128e44408ac2419c11545a454014384309ae081134c7280a4488a2834d70f498a2474b74a424aa6015508c1091482802509100000ca15b230050e3ef045122457ae2c6099542a47ce265c48d424605a106614543ad11e81eb1e157b04eec12ce51249f3661249036191a47991c6486924824296c45d19736e5c716fbca39cd494aba405bda1093092e8c114b0608210106882132068c205110a9a44123c01657b628a92164fb4a0f404125ad45c1c6421ebe83bf96ebe8b87efbd1c4a29c5c9a45c49199f9a1172d9ac530230274ae042b823695eac31d65af30e49138940211ae91119921441c427df8f2c84e0c512296841022958f148cf277ee889f404d18b003cd004234f3c49810aa0b0e6cc4ae6ee3692144974d60f498a18743793034b8428526c1cb2542251150ba3ecf0a307c8040ec127906887b88af49035f1ce10fb995714fb9034390cc93e33a8d52022f6f35826623f331ce42407f8450d7830c4ca3e1f442413b1c7e6675c1299c89bf926110154aa5c1926499ac824aee0e11024923442244d54e5406f57aa1c66ac4b240d1349739f54255966fa0192437dcb7178acb7f908d28ed95b78646f6114f824ae604b864958e2d02943295b8654ae54b172da7eece063cb5ecaaa7921d6290b59226998cc2df7008760124814574592061ec80f901ceeed73a83f1d888f20cd4790e9d9eb331c28777a20f23f407250e57061a503d929654ed2c04b409543e92d6fc943b22021d695342f26894b8a9838ac8a1762d56712e3f05846b61cb05ff791bdf4b7f9d07c9872249234f0a5ac921762c158b4e3c22a99b3500d8ea48127521292d1ce00070707a78b8ae4c4142427a2c02b0f9d68c2b455ca57a3051caa71828bc905d769574a2427b65800921358d0219ee54206098a15489aaf165c18b60a5b85a5e2c805d65a6b8df283aed5a278506b8d34d04e815303807e48518ca2ac6042217bf1d5232ac5083df802a98b1b80a00ad64492c4942ab8200a2d8c90248ae088abc4a4d907a7653fad9c538b24b4053d37d69c375a5cf78e7216d0508ed4134c3a7f5b55eb871405c951762305c04c2aca0f57f499e97999faf99a73e46c39a7167acebfbbf18e72e20a82d173ce39b34caa46cddd9ad05d4dd213b218694cc3df78fd909e84d1a77e484f9ed06fd3616f20e7a5945e649f99869d592488b484135ace6091de118d47484d24f5ec87d4c494ee66ae90c1bff7e6a3737a80f5bec3d3e4acf71d9af47b484d28e9f7906ca0a4df7b74be18827e8fa8df2d10b8d80fc9061fe8ce46bff8de1f13b04832415aa28b3b97905937ef25cc3df2b7a3139e6a52c6083f7f82ba3ec31dbded819fff910d51dcf577c54d87feca97c21ef96b1f8f8a4a58f17833594850acdd74e87b203ddce429ee991adc2a161607f7e260c1b1388054989999e939dfd18cc581a5a2266e39ba546f97f749c91e2e5947fbdbf0760de70cc4d0d9b54b5474716180506759753a719bf6ed2e7f4fd5a242714771cf38d96d8f77c1dd96bb1ffde4b9671cd64e19e2ee47976ad3b317b91bed33da6c1dea2e3c1d572dcf7097eacc45477614c7a1eee2824d4fb9dcbee4eebe8d7b8ff6ed9cf6ed9ad6928d52cf8937a9bc9d5ecbdd967d527d3af61c79c2a59b300671aa29eeb4a7da5c605edaee826f8ed462c3dca3b04faa4bf2da6c1b0527afdd4671af659fd47d7c898784b75270f5f6f3f4171d7c916880a5610f171717ee27eed8e33817972ccbb89f5c5c5c3017dcd15f47dd8deb5a6efa767813d688e05af28fe61e31976ff7deeddebb645aa671f8943b2a371ef2900656fa34a8d2f632ab1e37db84b41c0383ba8637dcedf4a671efcef900b36cdba66dd7512df7d4025f4ed927d5f7946aed8796bb546b31b4cbb76f5785da8edaeeb269da0f9f9486f9a46eabb6f7dc6bdfeeb55f2dcbb06c89e0ea9d88b7a7e892856cc77d3dce535945f38ef7f9075f56d9732a7bd57d54dd7bb3ca66f9a20f422db32ad657dca9ea9f94ae6955b5efa9f594e629abf79a7e5272cc290c4e25f6092b05770d73323422b81eed2fd909db6b38e636c3dcbe5c3b9d5a605aeeb2d24ecf919aa6bd5cc3dd8f1ca75b98bb3c480cfdf216ab6936ab60320384fa25ab60de633519bcdce69797bfb872f98b2bed2d2eb8fbd136f5b7f57079cbbb9797ec936aedaa979f6eb396552f2d77798b8692a2e5b7529cfeb61f784269a8dcb5681c172395b252bcb4e44ea85b7e7a41f529e6e5056605933bae5fcee11718989717dcfd68f9cbb3979e9654bbfcb4b560d82fb84bf5098563fe8261ce61b9a1def37298a35e0ef397c3a01e93396ec76b15cc7b5cfe721897bfdc259ffe924f7f71958aab548d1f2a1d64286e35b39a31d342a5bad930ca49371cd31a713670a6b5d6ceb8b2d7464e5c4108e1ccab193a23f39ab8a217ea70fabd27d32f8543f3e45036e0e0ed7bd9b68083c792c8de4c66997096055c87a3f934056afaa0a891d75dc5ffb8d7465ccd68883b8ee338881d3b762dde64c7b08b5edcf1599689a4584e6e56f1984d3f9b66261cbc95554ec439c6f1115796051c7c8753a390c1989a31573673dad3368dfe409ab3cf88ab591357731557d3e62ecefcba33669c133e5a9a89136e90c680524a93b8586cb032d871c39ac0aa808349535e0d726cb49070b066467c351e8d279689a41d382bf0c1992a1df8d71dd0ec977588abd769efec6b763ba0d9427db34ae725f5fd9581ccf6c1e9eb9aa6699a9677704e5cbd63f8c217556db0f7debecf7923ae2aadb926aee08cb87af3869725cb0a3656e9ccdab465cc33421a9c5534f8f7eb5e5a1470dd8f3da25232182643e77322cdb706062fbaa961835d451afba7dd18d453dca9c5e5ef355e9fb5e2ca5a38a5ed2f0b1e8558c495cd4631fee091bdb5968b51af232535da9e87acba6ef395e76ff0603496f085c2d1c360da106d3a207a6347b4d95e72e28ec669f8e24d8c6fddbdf085c2c5c80f8316daf8f20ddf988a7909fbe074cce3860305343b2615937fc0e01de22afe05f31057f1283cdba5a5e6c4d99bb0cfa3716d79a87b495dba4582eb26b4d84b58fbb5dd090df596816c679996cd67adfdccc5af63114684d6cf47e3c193f5bd6769c0c15f99540fac13d689235248a09aa4a0e1514d5200afa8b5d62aad11463c3ac3a16c7833043d1af0a6072924aea0892b9d5a60fef27923e3ef36726eecd0f085c2f58d9a9c1c979cb8a3af5f6b5fd062682a2da5c95c6b2d77f6308f81a192b80a22d2c8cbe01769b6a7f085c2c53c66197353a9ed4ae9f218fc823bd8416477421a4876cb5b7e6160f0ec971694cb6df199e996ec33232557b051d10625a5a92403efd8f0ac507956b4ec0188071f3aec60c30e0fcf0e31f1467933f4f068c0b75cf9b878e3a2bd66f74969c1b075883637e20ad6481af8fb1c7989e5146cf6846db9bbdd825f6f76b33cfcd864a24c818b0dfbccf4cd3e5ccd8d68332367959952a57b33e7827fc476757b36acd6661d116e38ecab7dc5218aab187362a5185523a58f0e2ae9f8e2aaa6a3b5b1e6ae87e6361c7165ed4c012773772da45164997c58e4c322b108e15a4758d139b4d32d8790aea63b9f1a3d5bd3364a350d8221e9758977845c57e20a5ebf7cc4158e1e767874e81e181d7762b4afbf97e352a93f55938a3455cad24b33ab784cd972096ece348c4570f0d7159f6968a219a52999dedebd29bd4d73675f5f5ce1eca56726ac7dd20d9fa39a9651717587e020662d3c9da2294a21dcf7ef9ca89e33775523c2f48cc3d7ef33139ebf2639b5b8e250291920c9e3aa97f1155309299e4054c854319c1614f6167cc2a6d2bd8ff96eb9fbbc96bb95ad57c7902c59b248690a44bb660ce034cd3db314651062207f69f8bacc50d35f4b5a5ec5e3ba7de9f2ad195cefd96e5bfe1e8dd32f7cc22db8bbce3d5eb738ea2f9c1d156d2cec5a1e3f5b72677f9d6e3de8ad4cd5b97eda7ec26d7a4ebc3afb7853e9f43cb46fafa8ef14812ca2f70ad05d2ef849991e2c7336a3b428af983ee20aeedcb89287f335713573726eec7834a0ce9b0136a491c9c8f00ac1c90bf5cc85b761b479ad193ce979a9bd197e60fb2044b71cf373b6ec8b5d62bf2e7ba0dad7815c07aa8d6117fea9a1e1a341731e0df8b7951e0d5823ae646501f6fb1aab199f91b9b971f36406efb1dce96014bbc52a18147bc5e85fcd6a1553d15e5e32ea3bdcf73d58bec7c11e6b562b0ce7caf38b2ffee8c4f30b28837c576a0bb2578be0e6959e4d97f4a42d7dbdd65aeb271835ba7ed6449b4d877cacd0befe625e8936962e89112765e26e31b4820ac422c30f83b9ecfc02d2107318e35dea19ccb31933325996652fcfb2ec142303c57ec93db24b65a0d835cbb22ccb321e3127dcc1bc40976710d31f34661583b2e3fa8230885874a4e133f98c12356c29656d4b5b12cd2f88babea3476295482526c528f591896a9fb2b9eb409539c3a420b87a7aee10cfbe9d8c4142ae2b8ceee0b1faa61790867adb4d30e4157322767a9d57ba038a5ce936cf9891417dbbad96cb3db233e52a5850b122698ab5d1f68fd6b49587275c71296ff5043fbd8032d4db73d884250a7730a9e95d7007a9a4705abb6f39dc745c79f343d2d0df52ee2e7d5664ce5a20841042d8527a57d3b0e52f6e875bcbecebda6bce54da2eed7ac99a86ab8637c5a54ea315991413599458a5ebb948a5eb4da5eda8eb2f07a256a592cb58d9bc9041c823d2481860d46bd8fe87bc8149240dfdaa7baa44e9ecea538389ae578dab7ad3d6438b46224d7d93ae3fe56e5544d773b9bb92ae41747d29775c3cd2f55ba69f5ffc7a66350dcf2f24cef517a55cbfe495ab4f8dc6def9d4f0a951d3f5d1fecaf2cdddecfae6179086d7d75b361cd795f9059401aea0b0a4b59e43bdead7920195b63769987dcd9d8e7d7d523ff385b9a7f6b0f4a2592382bb57d2d01abb32a1c3bee7665bf3fd8c44a2d015250e64d092811049fa76ab08a2ae2da70c6bec3201bd909b8ef90c3e2b119e70a0e048cffc6ead77c12b9cb86a1961a511d0af65c493cea606b77bedb5d75e7baf3d96ef7df639f1da6b2f8ffb09338c9fe1da17771b68fbd9d0ce0d07f69bbbd971a0e1bcf7482432d2d73e8b4373ab8d0ddd5b6bb7db4fbb236f5c501da0f610a74ec0755a578dc3b4b7e9b8f916775ccb182464ce0c4677f0405147f6f6392dee9e449b2a90867b2c432fbafb91ddc1de728f7d8f4c33822bbd333dbb95863234234a192876a69cd9cf6787f6f0d9b35bd8c11963a9eb00b59f33c32da00cb00a94c1fe3e273ef8a465445167d04adfceaadd8ebdc32c866db9e7fa3da6c12d200d2d5dcb99e89697724fcb3308af672d2df61d6462da63d95118672d187b767a55a67409d3bea450e100467bc787bc9152db6a4470d8378cc22e7847d2d8df77a54d18267509d73ee137c5ca85bb0df4b5817b0ebf2429adb6d770f7a4b47d862f86bd6e37dc6719c3f6eaebf00986fdda706056ae7bdf110c0a327a1819f0b8889d84f0f214660cbc2e2284d7e19b81004d1f0d7a0afbcaddcd1348bcef600789a1ebdb1870b564c0e3a2658ffc2d01d7751323d22a1eb2eb65df9e47c3c3e249dfdc4df99aaf46f1ab924477f31d6d807c09b88ebeab97db8e773390d8f3103b5d42d3dc75cd9dd08e53cb77f5f4170b6b1fb6093ede0f4a50fb6d3e2a095ee0430146a8b8738b2657bfcd870c45b801d66ff321b180020ab27e9b8f002ca1810e343090900084addfe6e30512c820881794620c8c5481075498fa6d3e60b28052c40ad76ff301b9a0c1138c18224b161676eab7f928428f9d62203c66d7d3ab72a8768a4016f6e380ecd881f820d4f7465a8e9ecbf49229c35b092b5dd8687b8e2cf518655b86dd84b36fdb2bfec1512ffec9d16364fa6b9f206d94658e6618cb742c0f61acd20542ef000ef39841da087bf6b61c726908636dd947290f612cee591ec258d96353cca300dd196df71152d246d8f6ed192e9dc3a61c38d3b33cf4bad06427db88a806d68d187ce0d178dd3d1a6ec460e8c9f01ac37010f61f84584e4a788893120f13ac2cdbd19e137bd8cb030c31858565223630618921a6306189217cc0c22ed46fd3d16384fdb54f07da6888b230981d5e8da4d9fe24cd96834a197b2f89fb636def38d3b9d05d1097f6793c3002086dcffd492e0765a41fbc2b19920fb074cd403c64cb0769699f69e5062debcf8e36447be919d5e68663a7c80f422c209cccad470d5d0faf489aeca5f8520e82495995232ca9ca12b8a4e81d5c12d4c407a1a6c7b4259ccc3ebca3fe79597a82b8b44f0c35300294b6db4d37e5a0b8e53f56a94a6fd94864617fac1e997b7408b57c1054fb3c00063fb4fc43c2424525af8129dd69a0071a2e0259d9310c44646559259fddc83dc5407462cbe3f058d98f294bcb673422308d886ab0b06718cb52bc2537be1811796f06204257c3dc1191f233a8c727521f212857e06350c38c0807a60c6ad822224aeee1fda0a867c68051d03b1153129329865836e6470c65d23d728fdc8b8305e7bdf7600c2e92a1bff7a605b27774010060a10b47965085298260650557ae932118718229529085122a4358b10208193cb4893d934008951015112d69f97824dac0cc34849e27fbfc630539e129d98d437da7e6207a5969f97b1eedf4f4fa4c4af6528658b21c344dc77ecf53fa3ca5cfcee3727a2d0accb55f99e7e5f5a70c935c3fbd25432992e694836ad0cd414ee6bc769efbf9129396976eca904a90139ed34d7721aa77394c862f39881e65ca41252aa567c7f28fa0df8ce5def4960c99481af9535ecd7339c894e5a0a0ecda4bd987132d0761399585c8152492383089a4c12225490ac74a1448c4ad2e150b99d43f16cd377573ca8acd1c953f56cdf749995912711182f146b928cd074953b01042c112466001116a20042414a1829e3ea87271c03092740516229020e292c0d40f690a932952928684e2e592a4ee8aab226924c4468000925578c88e3c6443d914cfce9110c38ec1456a600d91d991c1127305c64a15794314c355aa9cba1786e352a92b3ddbf40cc7f512c6177742fa76a76353a5628a5a11c872c261979881f0cc3ce62f7519333162d0cc30e9f7307bcb3397ec2ecfee12f10f6cf8fc727a07afe01efad7998dc14e6606c2137319e789f118274078548f790c19790e89c9413157a522a616f7fca8745a8ebde5d8df53e960773976975cfa81dbe38643a5833d88ec9e535eb5e9918abca992b4c315293d481af9981c2453392826c3214923ff77af0a7ef7aee4777008e68f058067590c4e1d629e98a772d00b8f390c0e1262c11c6e2a07a41ef31c52f912804b5430fcb1609e2363f010f844333a8247f0c90b8f79c3dc118157a02dd430779b045e380761b010eb6d3d68c30c6fca3df574c35a0e22bb5b3500304cca1852c156248dfc0b180621dd753009c34164775c0fcc3103c9a63189bc79ac2449e0152c712812c5227923e3f2990a07a90e44911a583b324fe1a0d48188390c87c141300722260f89f963bd50430ca298c360a298c390e7559828e62fc8c0aa1cb36f268ac94ee251c3fcb16092bc8951248dfcbd416acf67456a60c5b88cbfbca37aea2f1cfb9337414431f21019188e00440c8ff1577324917a80a5e56314d106b67c94126d9e76793344f527e32fc67156dc5357bde364e49ae7481a79194fbdab317a920626c77c089198c7c843318739cc65644ed2a4f0dd74c8bea7f8a7868e8f0117a98125938190b98c5f190899a71ea36420641e734cf6cd416ac76c249337a0ba5cce416a774249f2261573af0c8e9333f718a53e9fbda386afc768033191c6a177d429bc810dc43c9b180e35dc766202d65a2bf4441347f84194244c60640b4e38010c2b6444822d9618010b1f98c008266c21b3c4576b34272459c1e22a81c5364a5bdb7282176dadb5c285a44643c9b4983084cefab59820a5bb99a3212624e9db8f8b56bf1617443d807e2d2e92747db1898ed10691094f3ade4dc7bd6f46fd7bc7d5563fc0bd56175674f7a4d8faeeb56ed8b0e3864be1e04833ce8c5617ad9e33d7d4cb772b9b5a6bad3932dbb857c0b1916382ece2604d439c7e477289a491ef082e91371287e6ae66f589e58ebb96b4bcac226f706eee706a72b7ba22699e4a71505669d9c9243df070e83962920e7202b34cf268c8c7a2a424db3146180487583940b6ab2a1e0e7540b66beec11173100b828e87b9ab4670190eb164161afe69e1061390bcd56b696d6e30c1046ff5ba7b3d68f989b523b8970520a4000b2790c2fae58d3c8ebc61cd2a4a68716185142aad2dacb4b6c002c605e33df5217888131e7bd387441ce48467c33ecf835d0f8ac77e651eed3207c16b9a1157394ed2d8df84c36319893f718ff9839cf0c01cc267e771027f659ed44d39c8f454de8139a846d2d89f1e934ff630c83ee6a089b14bacbd84b71ce444e58079ec3cf3d8fc95836091a4b1300739e13986c51c048772517ccc4130076d8f790749631f54fa9334f72d352e7fac5725be83432f7f2c8bfa63b1e4861fab5f7090931d540e822ceea7bbe021d910273c59864b866846a7cc65f844d2587bcd491e12e40487f758581e426f3f6fc2f2252d1eb6948045978c543bf53940d6631dc9686770c5c297c988359a8ce8df6644c409bc5024022304e35e1c2c3857e69c096306870a970aaf6a5346814bd96bb7d6da56152b6dadbd5298b2a505157df56b696184ee66ae005b55a8b4aab42e14bcd8b1e363072e742d75b5336ad8b042d7e7dc2fb8ae460d1bb5d656165e74adf78beb45ced6af9585166dead7ca22a9bb552b8b28f1384e2d5b3d30d232c69b2370f1ca04ccc84109afc8b69beccd741c6c1d75d111d200ff2004f22001e4a3010fe44102dc06d202a38160eb088c06821a37a794474143f94ea83bfa17bb6220391503d9a183682d0fe5df1086f7dd1ee8ccbe62ea6ac62aa6a664662aab1c8ad3b697664d5cbd1a0e7523a7bd49ff60cd40e681b0a4a196a747433de7b3425f7b7bd03cf3a623b10c754fca500e1965a87f2496a19679062843fcdb70a00cf14ae0de3b9c777429c9b2ecb5c5afc544ebc80a2d2563b21caa6625086d5763d65aebacd76697b66d5849335d4e869376b168d16eb4a93183d15e361c36df2a5c3c7cf6a2bd68a3b5bde93330da8b36ab1959c4e00521bc16425827a43ca58e02d9acbfb64eb9b79bcf386ede8a357aabbd68f36e70228db420e07ae6b377355a5e9bceac0f8bae592ea2e9ed6db4a99f6db3f9a9836ea03bfb20b28d38a0ed68b988d0d0d1922222231ff0c010ab88918745122a1841155b4c210a162ced46822a20052c6c810b5dc842451596f6202259a8d0420a58d80217b2b07ec826206ac1c004442d18b0b4ff20c4d2f27b34d9b5bff366c15dc75e710f967f8474cf756deb2888a69f0f8bb6cff3f45e89bf6fce09ef03b2196d7ae8253c32d2f1940a303a5ec6dc057594b1cbf113d415cba61dcd3d30e6b8d7e24e888edc55b8edb85db37d8ebc5af6fc3c87b5415de3760394504e1c147129a8342aae52b10994424ac727af9a97ad925835f30e15ad92190242acfad752028a7e2d25a2f42de256107859b050e465c952d07c8704412129a3944b857b0d121d8f684961e4cd26a2e8f937ff66b48b9b13357ff498d4f2b74acbecb7892636c842bf2cbb7cdd74b25f7b4aadb5f6945a4a71cd53c700fa6110a362a8b76b8f508b259ab74cdb6cd0344dfba663b351e9178f9d7e9908c67272bddece07ebebd6e66e67eb71dd29ae5cbff8decf393559ea2050df164b37f760bfbf9e414d628f1bf6bae960bfcf4e9b8e3cfd8629dd28a53477178737c1822488c085143958c2c2369dab6d3aee766fb8781ec6b7e9d034b831607e62b9bb3acbf70281831708dc83f6c2b32f9541a4e9699e9b0d4254cc8cf6f5a01ed244fa6d3db8a1be84ae0bc84f0dad1da8f68fedf76898b4217d2ffe89a1b1bf0ccb7d2fa16ab57c232715572fc0f068c07357b8ab5f2b8aa1ceb344053c420d9ef46b2d81a56ffab598208a3390d2d146b47105d73d291d758836b15b4b54e9d8eff81f366ebc2bd189ec3dc5a4e3ff56af154510f70a4eab6141068515997e2d28a640e1041448ccf46b4101821948a8d1aff58451eb09142ce00a1950b83029adc089234e0c51a1d445105ca6d09488d1af15650819595a4d985a4d4c6919d70777662f150313a314057c09b83db853a21577b66c0086e98f36a6431bb44c973765c944d36356343d67832e5ade546fead96e4df31cee4c2f7d9a7e2fb71ca663f7d1861e5e87e92d267cbd055f3765d3b3aad9adde84e92deee1de73b9fc741376c12db8333d9a1e371cdc73a4e9f698fefa843bd3332e77426dd2d17e5dfbf5ebf7267c5fcadd9334c7aed35f37f7a4b6388b9648520207fd5a787079f07210a069a6109499e27a1f840e29a573ce5af3a387b2523aa794314ab9c99c83003ce2e984973df3315ee2207b50aa74a95febc9143a8b45a48ae8a80451c78c6a376a7a14ae39e06acc469c74a88e31e8185b50ae749c36db7e2d259634cd28d8b234f3ab913fc86436edd1783050a249c7a32c40459197c5031984252ee2460469d2c1d4a0a0b3892a99b0b7c92db9e0d24e803d216a9406712b89301aa545229e0c75dcac9412b05a326500ef9929be806b72d2e07541c3160d8a6680cf5e4b101482d12a91a1de0c40b21c706658218494d2f91e1fe214a26091444f28a4950416fd7210a06ba5744e29639c5b8e997310a079d4bae52040f7c4c7c31c9f5529f3cc90c2f981ba030e66c6978ad1c875892fad6a554bada28df628b59c8a2b793ce5cde566ccc193f8a66469c34fd260bf6e865d0b44e3155a4d92d87ead24ac682581d44a8289c6d24aa2889ac88296ad2482903177f7be80a355be43b53cc4892be9022e25732d42d145aa6dca5a6baf55c275a94e5dc65a6bad1d5a42cb8915fd5a4d9ab401fab59a44d19f49c515a5d7e20bb80a243bae055c771ee20f48036cfa1e804e5faf3461d2f31ad4342d7740f3c2807b58366302ae9be96acc6b79a51d9ebbf2e690896843af6938342d2e207e1e2517203f9f7dbebed54ad7b86d5955ba2a0779d371903765a12e65550ef1a6e3106fba506bb8e35acb7a68ec1b3661f519d0ecabfe566e62793335b2526afecdab6e5326c67c0b6700e6fcd5ea86e355b71932e69ab89237abcf58e10aee109cfcada59bf08d34d16a5b49ceed0f68b6965118a6b1d6473caf272b05924deb172fbb50380a3194134a4129a41b8e6cf18dab253e33fd88136f6e441a592f14dc6decf0f8d0e1c68e6823e96c1843962c59ba86c027fe88ab78e7bbb0eb0fbfbe2aacf4d27734f3375f0f3c3c1a7127aea20d71156fc455cc39467049ae124745449a6873145c079fd83925dabc274cc028308a0a8be838e11168448b3742244d8cb7017765924542f6b0b4bd8ac7bc3dccad95b6974850e9ebb309917d9d53ce9c9ef9d97a6d76e9f4d057e7d67bbaf5b04f7de2ebb097587b34649ea547837ee2ece513bb5ce2895197387b346eb65b8feb9b36717cdd74d8bbfc62979f70cfd55d377dc3aa97bfde52dc5358f5f2edefe52d8f46ea13a770375fbae9d8d6c3be9bbf8f81790c56f1b80ef3eb3059f572158feddcb773f2f53126bef7f72f3887506f47e10efbbd0beeae9ed35f67579bd8de9f70cf3d97b9b8baaf2f619c99b8a2572adbd872d8df2c1357af51af70331415ad115927a4e33bae53dc14dcedeeb7879a2869d8ad3fcc3c4803bd2ae0eee3013e99734e3a218455e013f828a574b68cc2b832e0ba9a7ef30aa594d65aa717afce2f26969a51ecde7b5fceddfbbe55df9a9abf5a6badf53575ce8b8299d65093d39e69da75c46b427d7157a3af761dbb5057a0d8d7c5d21e6532a9ecbd2ae5bd62c4f79348c729a3b440347731e2ee1dcd1cbe37b388117733d28cd24b1fad124729637e94ce78652a21e21271efc432df110666342dcbb2ac5bd1ed4a534a7734bdd174fb21d2d829ee5e17bd6537ae320d0333d8fee26ea6ef3d2ada60a5ec94eb6674dc36ed36a8869fa984539166a6e9e1fca5c5b9851f1cf2f5a702d80ecd103f47eac89021f4827eaf4f8b7ef93d1a517bc2c57ead191c0d2132a7c4387ebaa0cef13edff33e67e7c4f8deeac1c725a2b5164ed933f76815c2254532326fe6d1d61c145738ea917abb1212575a53acdd6e75abcc441b683f6dc5281eba038f742b8e0b8733574b01773fc306a95ba8e48cf5c4991e758a6804000080001314002028140e874422a1603822d696f10314800aa0ae466e4615a85912c41042c6184300214400004000606066d80024132398c883c31950bdf94ce8a706ca672932d2aa80d63f9df2629f587f04e79a563c0a6cd8b1f35f1b6f525fb94155421c6c2989f6ba392c8b41c550801451617286d0b3f73a29c7c4795f52d7803474a1959b744f10548fa9b08c4d153fe02f9fbb423823648398a69d2e7d68d8381561586f3c3166e14f964644c22e3d9704fe5e124693c4afa97de22f81004cef947f0ce01a31abbcbb27a1e971ef76c1a6bac6434f5e1b964a7a79a1bcd61723deaa4bb4666b51300bac1da751cc696c8e66801309c5cdffdac6de0ef181b802822afff89ab8cb0ada58c93c7dd6272718d09668dd4f02041670517327c070c72257199139134e4bb4be71255a99e60f245402a4b11c679d9b02a9f970ffa344697c6825b34b5002d6a75ffeb5871f7594c1e454363ff9d70a6859f0223a20f6448762a25c4479b935edc7cb94443fbf1e7aeb6db43082b980ede54350ce310da94cd54fcba12c890a6c4e5262698e93bc9bbfe91c37e88f175a5a2e927f0f18772008dddd7239fa160c7b9b3e2127de106198e3f8151b83e3a435cb1bc2597b6a03e26d3a2ac6b5673c3b2833f897f44e8f505c7475b570679a071d70b933185ad2a2ecc408b9c8f31061e20622a165e3d0df57df8a7a3b7f21a1b50a48aa36925ef06c7b59c7587c21b095b727a23a7405eb98911788a60a3d4ef782801065e57e1806cc135740b428f022f4ad3325b04026dccd26ca219390c2ce3b64b87620651844bee43c6c947c28517d43738111644b871c8cd9a6062042cee6a1f1665146e8f2db408425b080e1a2a887f8413ad26b003d20339a4c5f057a71b7ea050f3d647ab6e9a3ecd7239296afc961a68c46d76aed9b747fb6041fc241284ade634d9b46dd6849c22a732f040eec10396f19240c0c22ba8251aaa220de4428a0f984649c1242543592f53a807196fd2900c7869be0cf7f6d47b856e2ee514012c58c93a7dd085513f3bd6a3fe308ad671a3ec1cb386e949b9f4929f07d4ef709d71216d2ecad7fa28e207a2c191f1994241ff99338a11e2917e051dedc6e7d515d719998483a8d311e484986f70571460b2b5aa636c83e19892cb59cac0d416aa3f9f6659e1151300e8cd95865ae9124b94f28001d2aefb5059ee78d1d3dde6c3ceb94f689f6e22095ce6e316faa55ff1227d2a26723bd614bbe93e71776ab960e93ee49f121ed254619ba29cbb3d4a6ef822b435507e01a074a781bab9169a2236ea0680f2b148cc6abccb2604aefc1e50f2243315dbc68f510b59ffc6a6faac89d2b544c72268ec14ae337e0c30761cf2bbe231dbf81037902d7dd044c215e25566d778f80840e508755aceb7717157650ae078164a744f89cbe198b4572067ad80adf71a36f395b9493a02b2b19e9a6dbc1bde0e37707f90bfaac856230af17607a62ad193d41bbc1636d838f188e3b96eb0df0f979cf3d2c4a2321a4b1490690c42222eae21aa034367bc27f0d092083c5630f1ec095280532e0aaf5ff310f4588fc0579d87320173b132cca50ebee56953d48b1a9022c5a44c7d150d2224ac34bbcd9149016413f375773b7c79ac77d40371edbd0de553088d9c6366d257cce33061ccb1f571ebfd9dbb9cedaabc440f137abd34d50d03aa2271236a721c0966e269d32d806416624c4740aa194b0a49af5eae4fc14b4096ada91041711214657076d400be01b6d5d3a5791eabc336340ff3d739c9c829f4c30e9c84d7e1293d4b151711dc9e0c89cdb735e7b130c5ce3dd912410cdbb7aa0bd5ace880af8fe6503e0b336eab976733029900539e2da0ee633974db284965f480f2f0f642501fff4bb55e464b80ad9e71f54535cd013b68cc0e8af5fca84256cb670dec00c0d4c21ea23113ed12ea602f062a8b330f5ec20dec0d73929fe9ddf836eeae000ee829429b6a85bb191d2d3ea8733117d6f3fe4ccdd92afd896d36f97163335829ed319a606273997d22c708241d339049c48eb4973e7f7d33d12f3f23ca34d2a721eb89b97fef943490e1419ba54f90b0303bbbdf0c08145f3b0408b33fd31ea101d3c1cff2137f2bb91af1d2beb12f0cbd8e2647af20c74b73c33b5f1c8dbc0f7a5c984ea29e6cd3e656a06f301c6f6f36176ad4f7ab055b5f14c904baa61e5e72430724bf5320d80dc01f1a15e7b7520b0d1c06d28b165f0b9492d462f57d41ad83f04e739291c0471010c787bc57d9503f017824625e837f5aa44d16250ce6a19c1bb4eee8092fa4c9b14f4af9128b6e6dc3ebb167b2f7b23622346a73f4fc8a111f3b390888133dd6b1967d41006b154176f6f6fb75568823e8d3d6340e84d2416d65f53ad2e32a5ba86434f101a263b4dba5d43f7d61fbd1ff56bf3478b4e27043a2294142750914b2982b09247a5fcf18d90d4e8fdb328cb27c7cc7c0d6298d16401dddd57626a2080a0415d64c90c6c9040d24c9f8246ac25f760516aa8e0c01e9547d4b5108a14fb17554cabde961741c01dd84742bc7526c69bae8361d639342485160dd23d41832ab9b4e7b713ba1372b022e3d342da14213341484c123233f9b090017f9afcf830483ef9f00dcf67a9aaf11a74751320d44e0d1fc3e90a522c37aff1a134a141a3ca961073d06cf063580c2e6abe6c8e54650141633002cdb9c7627534b7ce5e490d5949007662a8bd9e28710cf74b29d4be3611cedb4e593a8508120121f32585189c1cfab8daec1e472d80f7b81efefff8bf00e88e36cfb570b386e46b7eb0bfd3e820083a7de5056affb4fcaa082be026b61786140f71e99625713447a6034e750fd0b5d8dcee8b71847696acbac20a44268d34612a177b077b547f02003188a2c7658ef56f8ccd0b2146a13de0a287e4ab2a3328087a0d8b4f47aaf81baf08b3f8b36aa254b4252aae05ce01294b2fd3f602c5349b73cd48051cce53e15ae796a816e8f17c8560812a4054080520e34066dc84596a353ca0770de3ae070245b191ae3097ec212adee19ac3648d2fe00f95d7d6db454d11c404c6a089360151a379f839ed3a5b5434ad78a6812c9aa5903bc07755e11d868d514fc42e15750b148a9ae55127ef3d9374df5c00d902cdfb6bef4acac82308a51923cd19500149c16ea5a2d10f74c79618e313331639cd84ddd4a22d0014bc061360793e9d1a894f37b9e3da6724cfe23fdea8244c429fe5fad8ab11edfb1f410271330d0af46818c9a0257c68e532d72ac09ec19341adfd2602ba39568007b55554f6f119b2aacc405fb36da46591a2395752dc2a26c5f50c3c09c1f6c647335e3fe1405ee2f45f155b41b83809c76c3957b7c62dbde18e72260a54e6128a4d132146c19974262d608980542b97ce099995517ddad9bd7f2812fdc378c4eea942b093f454140a735973413175697206635333480d18116b1124c4b2662bbb9fd5b4b968c6fc2ac7a4509cba3e236b924d624ebb4f1932314feb72e5350dd02bb0b6df1f0b7fb3a2102618fa74371033630fb01e50988dc809bb763dc8eef8925cc1eb2e39aa084765e3bc3a4ee3c5c86c87bb70abc3b61af77b6cc63e79d0674859f48935fb0a3e931cc15cb27940a6aaf5cdf75e06454c07860d6ad4784102b2a5635c566487191ffd55f50c1d4fd3cab89e8d74d1b9fc49f21a55a1598a1876c6e1e3a189215ffa8b477549113ddcabf19e7094b0461fbc6e944e09340c24a82038d8972856c4509bd025b9cab12cba0839db06ba63c3699df3a5388a3a4b61cef22ba16f125f1ceb2775c73d48f379eb855450e2d86b462e430391160b8dd00ca100741e5a48eab8e23e43cfa30c32fb53de0d46b0e7a36f52cc2692d3d5503c9f49c020828273c8740361480c2893af6ee602a1321ef99aadd04687975cad3c2144ab13135847096fcd258f291921850004108dd3952cb3194d306ead6ce7a9be0107be2ab7c04ac0b665551d0ed188e6eae8f8971518bd27a97603fc61daac09b931af9ef86ba854954aa84b63b582dea6c4c0068fb3c37490fed1b4c61feda700335f2a5e4b99af6f10abd92a2f9790521f24c5ab87677188695996eefd47cd88c45a1e75ac7e12b45f51398385160bed9217ab8e1bfd34d7162cfbaa7554b968107e00fc83c73930a90619001f287ab6092775ff7c8516a783c096704ac7dcb0dbd6e36f0c62ea03d7784d956e2a4aa67c5b5138afe04317bb6e41df78c7d6a6c4d0feb4ae895539c43245b260d270b1d79f11dcd2ac2672f8524b2768ced5ee35850c686826c170a837b9cdc69ca013ba42a438a76c835c23223041ae8e3420af9e4573d819b1e2c0e3c24ba80c312090f20fa8544f6481a1976b15d89245a9833ed9820535baae355fb459851348b6287b36df1223e189fcbf84078f06a8318f6695f8b35a05b1d1684b01c59be1e9a1418e9c624de331a23bd77c8a09ab5ca0846f10d884bfcaf184f255dddf0685cb0a5f9214cd36df0310ff92437e9a9861f4521155d7426b536df74442ec304ba6b659df83013ad04a61808195c60e8324b685f7b03060a3d7fd7d94eb537a5d3656cae9615ac99bb6f708e1f6715f393d6c6181bec19ca649d4b0fb4c1dc86bbd6901bbf1781ecf050380662ddce0fd8284f49ea097b0a5947998e95174ddc829d34386a402aeb912486813f8a16e5225c2d702b5570f0b57a5291ccc956c202a2c6c56c24e9d9e6df90fc008333ffface4ddb3a0b1acfe70aaf53abb3fa52c6e297b81573c42a35a6c4767cf5c69e9ed79e45a0200eb1b99841e90f644df93c1492cb4f8c8e9bb7582d245da82f839dfd97be48289d4f7424970ed618b27d48507938ecae9ad0823b1d1e2365b161abe7b416145491a076ef848dadc3f48a52d90fc9a64071ce124a1518e8a76776db41f6e4547ac87d743254663032ff59200c5a44fe9ff843ffc1f2e7b2dc2a6b6efc09a90bc6b88fa924026bbe662d72225b111d082503cd08a1260a5e5bb389df4824943cb032a190658708450743d0e6178f95ba85a9cff7187f6064ab9059b96b9824d4bca2d815bc5b8a400d82b3141438819b84018a99238d3284a1ae40e55c9f9cdff76657433b911c48aeeadd0e5f39fe1bbb5f8f935c4ba3351fcc3139bfbab50b04c6f3613457e57028b6e003729f1d41174df0ec932b13cbb3f617facbd08d18e63901e7371b1ce13d834492c6cf9e1567e83a4ec326aef803eb464bbdd57a024970d1ca697b01769319a6551860e7dacbae5d1995f27fbc435a940d74b003c2292edfc95ee628867f12d942fc7ad3584f17f814ebe7ca781e5d904ae46ba54292b8cf27cf5ec0561234ee603d400302547a6b663de396be45e1d654c76d287ee9d3c12a7c47bb491d18a62acc2489945bc65fcd9bc34653d515fa08ba34ebcae3dfe76680b0f8c69541e3a69ac440961a08426f10f8e0b1b82843f781cac5012e68ffd90b7771b95fcf6fa5d4175fd2473def8523fe3f9602385f5f7aa2b67a501e63e2102f60b550d8bfd560677569d088080f4026b306e2f7ffd2998a0d55195493633632f7e49412bf5fe47041c8039298d9779dcd828999a8863189416897d5f407d73d966543ccd76a09a31f6277e20a5cbfcc50b18ae543f68f846636807aea9226f1b36031ff022beec755d6ca75a7bbf270cdaa9d6553471e30e4f25d55a820b8a61357818fef94c812e9f7b2a2122381c18304098115e45d0aaf1d4eb0cd50f9bf9d3e9706deb2567cac9839dc55abd1a0bdb85b320a274c2dbe99394e5c06bd450e6d72b840c10deebae2ab48b570759dd18a8b587e72bd03ffbe0192d85bb6b69d296033bfa32f3a937d1e4a7374a42b48152749911804763cf221ff390aeea30013a634c39729cdd713ecb82d6c9ef2e5273e530813fd8b285193854e84d55adc3f6ab673340a361ddc474fca4f833fd81769a21db4e92ffa79f6a106b2d02dba0705528c6ccd1d470b68cad5021a3255ea284d8e52bcffa7ad1bdcad6dceec9439988b1ccb8334b71d40a15f561d48ce8d0e38df71d9a9eb954c70e7da1c912c6dd66717f4ecedfae1fb8d70493af7f64c2bd57b32826973d663ab39703e30b7829c796ec12a236b665472632403604045e5d295b59bdd930ce0c55651657cafd0094fed41955e6b3f30c6b6871f165f4bebda8d6835935d3817db9cfdaa5c9247f07242c319f7bc7eb52029b7791f97f14ff2728e74434fef49f428b8861529f0a7eafb2337899b8593f10df0751611a1a7aacc8c6e6065c0abac1849ce7f30763f70e30605aebeb0f9f84f845c141857e445465a1f78b10f868d0554f95b03f25ca823d60152f1b0b3de3ade326ffd2aa6c4c6a7d24723ea6f651e79c82c7a3382feb0ee13e04d1417600a59ac878675ec8f0d833c18e4b0d63b2184504cababd2f080003d49a8a818f625023c7b08c80deb93854715db4b7a26cdd2def77af4c06a2d9e8392ab7a8501e38603fcc2fd9c17520bb190135537ea4bc49767ec731f304250f47a6c8f775a71624d36f1228918e77f497af80972cc65862e01a8e3d71c449cedda82374fcbb675e2a52e38d66e9e0a0afd66572208e99dbe2d159fa3f4de19b45a7ea2c37ff2bcaba265878dab586f7bc377f68f59039b2a64c0dd60991f37ce33b88f73e94b4d7d988e6cdffb5624f05f29bfcba82040cfcb92fcd2f1f89e05f57ef80ab1cb3bdde78110c19426b3c004c52453bc5f3d0147463c19850642560ea3a2a315d70936e006ba5561dc1ea308b1f313918558093928f48532a3267e9c2e74b3375d26f60c7fd75659bd7f3c414406710b9448a90a39b8225f0d74e44ac4d42512a694741fec9a93f87e7ac6c0e5d81a8bef378453cd47955fecd31da7ad3ac288bea380c9f2e7009fa3af0bc7fdf348db9b9184abe3d89f3b86855a43cfa90e7b9c887796f37ca203e418e791294874e56af19712aa22edb76dfc95d100d9cb8beaa2fa25804d62f2c271ecf2ba0e15bfbd4514aa0c368f234008d87750d8e4a9f3882cb53e639e86561b7ba9100cc384713427e675ae8d211fbf29ddef8782e3909091f1523bdb4f6081598b7ab2d0f66ffccd9c8d998275d4b8a6a3198335ce3724dd88c808e876bfb141512a1784916b26128918ffe74834c928e844dd6c3b35d21e15f0dc1e951fbd9c79392b0267052878497e529a336ff5dcf39a80c9a282c1e1524b6136f7c7200c06f612b5a20ee12730bf3e47c9a1e28d15af56931ad2114233d6587a5ec70c1377dc89a0ad334c232b1b0b918acccdd01405ac684beb45aa655b685a8dc237d06448f89ad067136cd77b67ab4e2f6e7e8b0c8a5c12600c259fd2ea231bdab6f62348ff07abc367f95af7ba6fcdd3ed3be00d6ddc4ffdd0ed68d6fe35955890cff8909650248bf6d2f7f10d4087306690c130b60064e3a8fa01727eb65db1fe265009c1f9f0e3fa99923dd6c9f2fb880ad082d27c41e677c54a71373ae15ab0a1494e5b8eb0f419075f0592ade4307985cc3cca3a047aa606b1e41842af55264b13ede73b4dba2afbea12702b80f41c9dc145ca4454037300a52f3ea365ed92e5be894b36bef9941950783a9b367b4919b7bb4f07a6db6a37a49a9f1411261d80f5b1dad5a29b3f07df6fc1f9c19604447b463f96fe0c01634f71e350911e6a4530ca6ce009ec0663cbde31d4b427ffd1cfece17577a93d409499c51c7f1b67aa17ddabc01370380f638724d7b768b118cd5d879ed82608420395fc71465844c4a646fc9ca6c66a72d0fc9ef2e49e3bff2f8b87ce319435cada367394b1c09747a1134c22b391a211a619fc28327580be676131e89cf4fcffa9e44d7bd482e1280e99a52a8c69cf220e469b49da93fe43446170fca09a334c6629303ca81f1356c2bbeb9134de0d8c65007d7890272c6bde244a01638e2341da1bd661473bff8b0b5c704104b90215162646a7c28ce6989c38d8bdf504dc4d7b6b243c155d431c4bb6e5c575dd32271715d5e01db740eb2f37b55a49b26424fab49c0f8fb929686094a79f61789c3e0295211890c86e93a55eb086ef133425f80b513aec6dc1f6c9c191893ba538f16fcbae02b7d46f1a90124b09f96da91d7a4ac2a78e315a5afdff68dac77f50b3f9ded261bb06ed647cdeb4bdf5851b5537169fa66bb24d031713c35b39864a4526074e6c7d6ec9f87fe11f478e5096f745e1055fa3fd4d02f40116dd35da9628c14e3389691685fc01513ed37d0f4af51178f6e62d545b6a194e229b0fdb17a934754d94f8d92e76c21a663b76959e87aa6992024fd81499351569f6c852e5ee045352bb3197d04c24344cdfbe27e23a5c085be4f9b77d1f532b9a974008f0da5677a46e6bcec55898ce7b80e74c1efbfd2050a4c12716bb689bd1bc777f5f3ba440b07dc026374b570a0f8128aaf3fb227e6ce234e68013fb47cb0d5c22d3073863ec2331d96f92fd929bc497c43981b17a97af3d1bb90a971f1718f5ec39c7ee899ae7fcea58b63cad433fe0ae9babec34f125de5cfc5429eca217fb0c96d2c20834f9d6cae1446e2fe0f68b4d812d74aee7a6d705d4f40b2b517e8ff5672e8bf423ae895f1a322b9dd784c084ab9a315238b223b7249de2a9049f6374d203cccd0c1183a5808b51b2bc92d7266e827e9f2b35971fb7327d7e82b88f36e1454a322cb325f85cdbebe25bf72ca9d7b92c559a1b2d01c8bb415b0b815d84ad76b5a16bb98d2efad2eeaaffa667f99e1665dcc111a962a5bc3c61ec20e3a99113f159e97e0650b25bacdccabcc8b702ec732b9f48e2f79a20895f94754831ece7a86d7851a2abb2dced64320c02e10b0f14d569b87af2d3a17440dc296f87612be60a84a76cac02d2e3944240595efe8a792049faf9d5a8ffa5d000ba1b54c21de932ae0b3b358885cdfbd2dcaae02c0334df33e79fe9e6abff8533a72cc76481addcf05a90717327e854bf498e91483c870a1104930c7c4a7b0a2c38bdf74e10fac0e3471abc44ba90e4706a64a31432d7a64508601008b5a2d91b4b74c09ab8aa8e3ed7b46f340c6f324c79b6209cbccc40aecb7c16e53a2e9400436b03afad8f3eb617fb7f2104116a4b5bf9637ddeee9c1a8d50a709f9b00e3a58d4c38a581090d6080c71260a819fa1045542051ab6faa62ee222d89fd68b05bc0b08f15a2ec26ae76719d9741dc167eabb8eaf472e667ee818ec3d802be5992c5a6c428a71460b84dffbe4eed726cd8b85d39a5274ebc053998dad98fc269faaf072390eaf3913b3aebf5552eb26795d6c84eb1efa7c296fed60cdcdfa843c7ec948a6da7486727208353292f37157b96d08b8d9bf48ea35c404491816108acce63d4e3a68472337374744a44ed653e41d7d6593ef0a03be5520d8ddcebc29dd0f86487c7e9d3258a8cabf23401dac4a61fc2c9cc1e1c34994d768a4facdd38e45f6bc387ce7e6cbc4523f42c68780bf23b0ef180d5dba121ee533604091cb77def840e47f5491d50a6b7c2adeaa9b2140f5a53adb2856f270eb1470e7ddebbc884f0c0735458a0e26e58c555917d3b35a36123a16ae41b3386a83a4e49ba80c27baa5a78a6e3e1ae31988fec1eb7934374b763b1e53c6c18e2086a95f9708d91d15d4a5a1b83d1b863a80ace10dde2b53e7b8c94c23bbd8c42b4c3c2c3560129fd56d991731c6225019ca7a00688f549d9ad08a57adfcc2942d029f6a198af4d9656c9ebedb340ab2d3ccb3a55476d787eac4809990e80ad4772cb178cf8be360d23ae025c109eab5b30abd17a0508779c55fd6a40ab7e8486c1c2f6a754792f537ad708ec708d1ebb908caf9b9218277e225a787ac476980e50a99251b59a0b4dce5e16b44176984c73e4c245080601603bb0d056091d649e29200b245cfb166c20766243a4fbe777911bba9a9a4737479014dc5fb003fc96b32961ca405f68e205ba97fe0818868a40090c903dfa429e647f118742d6d12fd089784b3e6e84abb8d5e75bf9de74586ac5ed92c4b6d53141915aa46df7eba1809f8baeb64baa916b8f48014c6ca7955e9c908c808927d32f91f13342ba5a469bdb1c799cf14a5345864b5a52707b7f06ce1d912cbab734b5ac1a3ce8f0fe54fdaf1c26fdf12b6e0dd85480de4fac4e55a58a696a3398d18aa31c87ac2eebc5133a7bc2514a130ed1c466ee718b0996e27d041838544a1a229c7299a422a5cc23a8a29e4256d4683fe01a47306e5d0ccf9e103d389584908958867117a956af4aeec589ebc8406fd0821bcb8d722e1ca0d734a438eff0b05599940d4e94a299dd39c521bf1e1d99a6ee41dcb293e3bf40cafbe08dfd07e145231d1b03461b76d3f157e28ab2f08f5fb38f21ce173c978776c436585ef4f6f071ea2900743ec91f2f482d2a0ffa258da1c3540e59fdab53681ae18321cdc0dcab87d2d8b80611d251c3b662a43879fcda4e13f25856386326367de6bf313ab5b4111fa6191166692138a579108dc7cf2bc37054dc8ff41d40b694d45fd2ba886eb017c107c2a20f1ac2cc609eb8e774432ee09ba209f6d909ca3da4598a9909f43f133c248d801cb4147470a7c74421f4a40c678aab771fbbf3b6d4475f16706f98e1b2545f41007e482f0930a14b6eeb6724de35532ca1114876c81b4c5e3dc4eac47dafe2a257c5f83651eed3ca038ab363d8191826a7e07c6a74cc54e00e2d99c99a865949f79acf7d03fff309e07aafe12c0e9d7714349c8f625a6d29e15dac4181b71811cf5d8aa9ad7669967e99fac8f3d2efc7d9a2d727864b6b2f5e5a139405e09d18e7b6982620d6db0de0b710f14e987305e4478c32fb6653ee1959ccc4ac585a054dd5608ac261e50cb051542667ab4b5576a6a5a66d957cba07e7137dfdba290ed86a9f826ecb8dbae5a0361a7bfb7dde4cf22454f6d3c1e3d5bf1703eeb2122ff180c5330915b252a8cd129c8825003fdd00aa5448ed5b1a126427f82d645940aedce6c84544bb21c6891cf9c8ab5ebf8d32c207801c872ac25b51f4fd81ab6fba9a04cc9120370cf67a8eb708ee7d82860c977025ff300b503002f9d2a32a25f154464585741b8dd594b350ea4123b9936a1b089911e02aeba38e00346d2468ee495909e03e8ccc10e4faefd70793561aa51d6ca3a7910b441f77811df148555c71b2422c451e12a207794f623112af4092d74dca27412ed9454fa357af5a0a1c9eda58235dd295af9296973ffe13fae2d087b91ff4bfa1bb6414898e56113456ed0fca7754a3529eeceec7e76949afcbc440d8b1f1582cbb6fa24601b871adcfdf678b7a46d0199f6f5f3328f5a337d4ed78496504b5046a9b503a284b6bd9bd5737ec3e5ffb3e29524d2664e8cc919e336561c0aa358b3be7ccfe98562f28674b6ca02740525730357aacd3562a7c61390b6785e15b4301cef1ada47cdaa6474da633b30a79e59b92e4c95885fc89c455df61b9fbcb4e0e79be6242e3c18c6ab0bfe28efa8ab269a6000e4725591561953b73c952d895388ef2cd3d6aedf52d47e714375a814b1c8709fef3541ddbee33230a36fa5d341be70520e66e0e22c2e018ed0dc01addae70a4bbf2b186c0b4d4720063de304aa7a1c615e30b9c21def94bafae1abb477e3f91e45cfef9ff22f9906591cc210578d61df5be2aa31b1aebc7b92e3640b3a3a272c2c407390254320bc9e81cbb9e24d566abf73f1853c377ed310951325199ca95dc8ad5b95b495ca301af36f98d6998195250c0b668d45a46813817d818b02fd5263ca83f081d14a4fe4df656405d1b181572706ec726ed34b2f5ef499427d4de91c388ea66a41fa53155a0e39881cced14679828601825c04722064913630cf2e27fab4f3d827123f542aaa04f514aeabdb9f68c04474c3a781e18b0b898dd2b57c643f55494f7eac279f657c2d44caa89cb55c5e61f987ab0b64b8667542c4d11a84a34fbd81aaa6ac85db7b9fc73c9f8cd88768d68392783646071d5a04c471b2a48064fa5ee0a8f14aa0728ab423df88328eb070cac034e7a529e683082b89a6cbc36515cadb97b8480ff0f4c4e67e25fcfea841176f5911e2bc282f6b17fb6dbe7101b2b34dcc7bfa3fd6bdd8eb368531d7cd74fbb3d539e234fc7786f40a32537efe5c8c54b1fee58177897dfe3813767764d2ae40114d03c5da8bad743fc4c6153b1d95b883dc3a5d2b212db15e64cf2e41e779e61277bb62b4a1132708d3d9eb3823bad9149c03721ad9b42e405403f456a14e6b3d75f89facdfa68571ae057497a1243d98938fc2068f1b993f2aaea882504f673564e63af88f9211bda915bbb459d67bb797be080e86eb3ea97aa4d3f38bbf74d7779d2181e456f93f7743107631d09de8ecb154804d777703c9d2f2af82b2834856f8357530d02c2bb927324bcb82bfbd8d34d819e3a13453ed0e363c45ebf7a5bf6d105b1153e781e0f9119505b30466b41bfbede4fa4c1c0df014ee7c5af7e7b07c5174dcd6983d9cbdbd848f431883e28d186f96c5d9673cac4e9cfb492bce0b1b04a0e3b5739d8cd4e8cd4a07c45eade122a62e48896b165734891258735ae2b355932938a940252c3efb8b3c72e58ab40d9d065556487404ea593ad1fdf609026d6838a7d0b3275ead246597cd02d3cb981f4dc759ebf154a5ff235c591044973f7c47767ecca733b8a3da1828b7bd0a950d548ff0bfcd49628cc422eb54baa7cd438d1ed9b416ee6f2fccb6108b29668c1ae1e743599a82609180e4aaba0394bad553d6407a26931b208e9ac7058342e363019bbbdb7a2e498adf88fcafb05c7ef2d8ed09b1ce17883cdedd5462f0d4f192db74f506337463f3a5819b0512e5671414e731a7fec56900160e9222441760a7eef373e00d90e546b34c94f4d5e837173437d3b163a727d17d344c304ee7ba88024904e5f3ae7e2b184928b50c5daddf13ce8ebe6155adcaac74f844763bf15ada6bc5378e36f117f9e92e29990513ed215937b19ddda3e7f2b67d8c7e741193caffc8a072f23c21d4feddea5a5a02492321fbf84de8fd2702c938dae4c3014ef7a514ab6e00cb879fe5cafb76639e512fe80bfc1120c3cfa762e0730a150fdda057a61d39102b2ae580d5f2a40898dc7fd01da8b416bfb054162ab248496a2ea2c77d2ad63f81c38939f7d09d7a1465467e0f15ab956ed0e2ea11cd0fffbe0261b618f7166b76d8c213e35e06e26b53b05dcda687b72ea1090420ec7e1a3437568c3bd41d8bcce9aeb851810b9985d161bf49c99c8321d66dcee3b9e29df735012de2f204858a634b3627139b9499d4199315897ac396659145a632dcbabb7a96adcbcb3c6a6173e8db21b17d4627a6c989afcb74e8564cf9cb0b5a3f1a66c5fe7dfbc508800120090b281d9e2411165080c4fcb32470e68bd63e5cc2f0ffd0d0179343c8d603af1503340590ac930516878cb2420be37c00e2e119338e72081ad239f64d878150cea5437e91591f1fe0b24f7628cac66757ae10ff6b20ef4c1a8eadf3ca06fe767c9e701c996828d9b4715249d98fcc7531f0a1d3885ad7c09e7de5a318b5f7e1edc417e14bfe27827f0b054ce09c1ba9d0d39ee22f6a273a0abee078a6b9ad5192099946af129b1dd484d59722a9c83341401198ab839a390b3e851e6ea01384dcb0f75e029a5816dceccabeb2d2ca8ba4e1cf272d02bee3b0cf7ab5f4fa5fe6b554016bee01aa68083e883e21ed8c45e2af6fd268ea3f5ebe15d1ce816a4905807d94c8cdd7f747f89b6c1ee46fabab32f38d5e03200249e76a75e763b8023a5caacc72b919e1bec147e8d567d6991714863a2470c323b811dc246487a46fc085dc01526ce8949c6d6ffbbd3c6864b21e4cfde2012e28bde8dc55fed540a54887d625f7ce247e21fc279fd34587573a0e278c1443b0e5352110fa2d1f118a45cda88be3183b27fc93565a0e0f2b683f4384aa61a034458b424f23a65a04f11e7c651c864458ef5885411eb59a0e4059010bbc52da451abb458e1c44f0d5813ce5500bd91602a8fcde355246b432acd73c927cb596c7dce73d8a6b9f3fb9eb4fccd806eb8f877310419c9cdff6358101c5d782afba906e77c9a0f3fc8c4bba01f8da385e07e2cd6f31b083632c12ca51a1c8918c71796b3060d99c0ab8f520d94fa9b98a2581d3a44504f6622179d113d3be2807e2570e9502c8326ebb848023f4df7adabab2f68374b5f10692cc48b5f2e2eae04051df751a0aa3645345220f6b19344d97e60b2de4c4a825488324446ec0e22abedcf19b91cc05eeecb24205856fe9d6e041b3e7e4df0aa61805a87d8e4874bbde77558f0a5b0ab869c16a31dfabaf36a2c8a664011ec89ad04053a177d822f991c9c119beab294d7ba984ac0829d03d8d6c9f38c7459d1d51292ddbc3ceec758e02b87783daa8105d65ca8f2b331ec5ae0df043bd6c0677ea08da510b6d5a1cb70a8234bb83a315c7ae41e280b213830b5d383293ab93cb86be165342e9d58f07714e8214faa951ba2702e5dd735e241216ab706cade05cb68519f99c6ebcfbca274af9e3b14153a2c427a7a449d39129513ae65110a9b90c00223beaac12fe548b059085b8b8454ab4e8216f24a405858f55814023c0514709e848cccd559e5c94c91b44afc2be7d05a4b22a2bc2878b8377f46f75fbc0af9f66e1cb018acf85aa80c6d0edde480665639643a1799ef4481c23ad4b8ff38e13280018ece9983e4e23e4783467032e21df156124a130557d3ea4877bf973c8663af40df06d179b48902353726034bf9a4966e51f55078ab775dcf901765bba7429cbba4ac9f4782329d446ba2e02cd038f462ec353d7914f373b9e0cd2039037729fcc59cdf5edd0a304074b09b7b2fc57130e091ec89ad4bee8c18ddba2ec16aa10564e1bc082be380d8d4c817c2e667093eeb4e4bf4701aa0b9a4a5ffb26124c273720c30c91c05e9fbc4053020bba54a5d221d345834538cd7cdd91597ef24b5898167d599983213be2ec53ea6536eef0301069d1682e7108037fdff469182752cb9ce8200cc4fa25a461d50f83300092c265dd4e9864a77fd58ba49633c902f3906a4c6bb354955d4ca363b8f97d79bd416466ec683a0e9589c5f27d1b2e1d9a0d7db67073dbc821374514b0b60d3c114740c650a07cb2d5a9ef8b67cd0059d1a2741f52645414dd0552d7bf05ff027878e334794bc1ba4d15d86bed4a0c6ac0fc4cccbf9308e6560daaeac914b8fe34f5004112737db5b67025f11fcd4a8b16b6e0bc6345d91427f1f36c7606f94d0ca26e4e0f0cdc775817e80190d327a0a902fd3b68d07ec59884001d467e48d4836c089a8f3094a28ff09a6a6e044f2b76fba5f8bf1d21147fea5b1d41b20ff046a2b689512ff04fa218c6c41bb0e2fbbf3e025de297fa524207e141d0a8eb2c11990460428f63a7328e86e090ca87fcbe2346507065aa805251da97636c08e28e5d5ff0dfda24f549e186bfbc24e4c1938d4c94b02833a81edbd214e72a8e995bfff195f8c5b9f1722df60875bfc1032ed5955a42b448c97c4bb1b338c7e8f42e3585f1cb107543063718d672e0760e1217bf26c002e84d8d8093ecd1a8274c35de1e1fbb8a990db00c70b55949fa8ed5372114804d826606deb2016810debb782457b49639d69156744ea7cf92ab67f39af0d0b67d400a55473a5bcf80e7dc9897ab302f857770496154fe572f20df407dabecdc25903bba241b1de18ca7485e653658f2586062b5a72ec92e0e760a19632cb31037fce782b2c132eb5fac2b91eccf55a9934b01bb5ea93c68213e5dbb41a2b53a6e667457e21efa58e9cb5d379bc29b1ff5e9105bd13a2f1585124e0595fb83986496ec34729a04af94e430ca4890514068234d522d97ae252a0bb43469575314f3838f54623fb6d2b772241e1fc741597f2758cea50add295a39be4aa34d4efa0a38b6a9b9a998285a327121896fad7b6e238a0aada0347dd7f6a098312656091c2fb1f59072b2a7ddcc4a14a80d82376a4c47e4799f67cd0b0802444818d55cc3346015a1188c0d6ed0be54630ccc5478e2e06429884f624e0a4f734203b5b49a1c2212a6498fb1e1f25abfa0cb430b7863631a3f3f1134de7044ae44bd1b9c9a7edbb903885bc2ec468dc00b7a2cf832c785d910dc340b71d1edafc46012a6c88e8c84af2f273d7a5f16669fd082b89ff65b37e1671bbe355a013f0bb7458a84bc62ac97a68e8cc792fa501f198229b86d3cbaea8b6fb365024a4c7c411baba8456311c030dfcff28934e57ecad2820d759787fea87a05503ea818000c97bd7e9eff563ded23adfc35bd7a0beb27e83887d57d31f41a4cc63c67a6ce1e43e6a6a0e82d1f7ac26917475bc91c1c333562b1fecea27a608a3ea761ff2362a0fb5e29ebf090fa0c453d3a76e71b14cf1d5faa58c7724665a3e9268bf025b309cab1a985df0f7b210e37beb02d0214dbc860577a2858f5d000ad75f4eb995d12b636105e85b40d3ec60c72ff8a162de42991c6dcf37f8572f5b8227e8564f0b3514f3d2240706042472ab295a4101ac3c79bfe643da89c9e39403e4a2a2352d36d5bcc9105d7f2ce5676bddae9820eb49bd864e412dc7f26e313bd27655465f2156e2ea11c3bf8ad82284aeb4cae26adca408002178b79879e37bb7a42bdbc2c8d4eac9ba642afeae1b57b1702bfab98ec9c959731b95d14316fcc897d20b1a18c4c2822bc4264e00290038601bfb1bf71491077215768a4d9d6860baf6c30c02ca3e8f2e677fc3f13bad61fd1d48727140dc7bcdf5fdf6816d5874cd1ba5a008728c619ba12fa5aa5f2d1ea1ee619c1e46e33854f42d6b2b15fded4a49ed004440016de8b7fd617fd12559b391a5f2d4af27715bbd6c87de5e55c9631be259d0f4695b9827128abf8d31d069691b887500e8255fc055c66054ff86b836dd3059869dbf45434e07d240e6ab034eec37c85ca926614a7ecb577e60af586ee22c0ddf32e5d6a8c1e752e9ee6620653a42bda4870c434405a3ddfe504f076528bcf9cd1b7447ff847b89a40d717686aa24943ee547a0c64fa90116c3af38840f7567a64986e323295016dd44b09dda9784b7dcd541d7c370064d81056efb41cd85453672c4c22afbd818a03a4be7a3222f381c49bffc0b793488a1edc81b43ab43d385bf65a9363d3a2c5b936042cc31398b711f8799e7db8db44b8677fd3c7d8bdf0321a0a19ee132f377736a0107380da032d9407413d982869c4701cd9b05e1707e4f8120b67bfd2dd5e285e864d436ce5334a0b6afeeee2609018f19fe36a0bf600667a437f0bcc9f6df8f124ac394cb6248f8c398b0e72358df6946bd503140474ccbbe23027bb4c9d666b59760e4d4cd02f6e93dcfe36b2d7baff106eb26dce9ef13477d54c8b42db36cc73551f395868904cb6e551232aa1188840fc905813b1c7aefee7333f963ffa2df3d1d658fbad58d91b840c8f2b5005b90177c20515a0d6db93e2ca6c6deeba96b3f1ad4fb1e708890f89f4bc45613788b124b0031ee4c1931880f92af14be08ae86432b72b10ddaad32f30754cd95d97b7de5b254be18a276f8cde9ecc6e98576742a4e89cb0e47a3a50368b541419d8924e41142e953b5e2aeed6afd499addabfb1460eda4aab89d2d1a33388e629a280c97f4a2213128b32862bfaf3b04b2b48a5b57a0997f5b1beb778bf0a116f7322ff256ff10f0db8038edd29ae84b790ce4e0ec28382d322c19414b3e6364c3f369280b9434a4cf23e34a8a3b428e792706d29cb13d58940cc65912c6f5cc423473a999907c721d2575f9a4164d1b241eb10d4ce0d7266fd18a186b86d30991e159468f8905a0bbed9ead78a796786f101641c24ede93b44057d456d94080bc6fcd0d1179dea03c4b604ae4e9bd3f60e7badf6f3f6637dfe1ec51b6d5bfc10f90e9bf04f220a80a45ef2d56f16eb31d79df495373c6bf99bc5d4777c3b5a9d6df5876b60ed366f70db525c01711f69c2a84736c2a8a790533b4b0bae2c95ca172f28937184e16ed545a5e121183c91515cb984a31c80cf8b21076e545203c934c755c77cc8813ad688ea368d9e385b5d4f0effa3f03ce0d15615a5445166dc61406bf679bbd73bfbde03fc6b8dcc032e1a61880fc59ffe039839b603c748812c50b533264fd6401e92dd0a86d370ddea0b44ec1ef736ca869617fbc4d3939b7daccabbe0e4372a9f3cceb70d7bb000de74851c3a93e7f3da06f1e75008deded2b010b067e2751deab5ae31f1cf160f4315d6073cb000850f8daf481cc726115baf3871c739846e8c27480cde323292f2bafd4412937d58aca8a4a4a25eea7434de4a2cfca1c24f8908fb07b9156b610d9c700dc6df4e572a86811eab4c14edcfccbc125aa42aa468eaf6b32313ab0e5d721fbf528396be2ac8daa6cd59fc24c895ae9e70ac251c6f6c779c433dd112d1ea326affb137995c0898ab255c77c6630bd50c0c01e563d76e7923ba873be2964e2f385694f5a657bd3b6542a1e675589e2f3d9764aae533a712e58e1fdfd8fefa97406ebc748471614345f5be225384debed4d72dae3cc94805f5277fa2e87f7608b917c94a4c6e4092c4d1897656d928476ddd44aafd5345da3cff8846355cd17cf587b43186bff12d44aaede9c4e720fb754e8440532e53ccd919458331d35127323c3020de22b2bdfae13ae44c9caf5e0de979b7f682d63269a30f6860ee529b946d941e20af8075e4c627ba42dc76be452d98a95123efb66eb2a32bbd1142e98d206d73c2dc7d407f0b6f11d84bc8fe74da4130dff8a2423dac195f14daa248b8796d4fde123c24c6f23b4bf4ad20586494014dadfe44e4249a65658f80e3e6d6e0e2e069a353644bfcfc5043618583d7a7b554d16c133ed41b4b484283dd1f241fde6d735f815cc56f297407762d9873729a1971478367965e155db1026e771576a61f86e6d2cded878591b47ba1cf85cb48a85ac904aaac73f017adbed62df98161a0d7c2f569d08e9cbb2d3d51ff6450acd877e717488502f15ec5f3d1c1cad6144ea29265706218ceb980e501c845edbd5ea19497b89d8060b555cc647339be949d240d4d26445e209634c5b6ee4b78c790f72e60f35e50675c3fc4fbe1b9b80481bde448536657a6b40f151a85b2a12676afb2306bf1ced3f6d90827e9a1cad382fc2b0c7c55afb25361a9bb3cd7d918460b5f13475db3e7c60689b1620ead25c4300634a1d5fbcedfe6660c4a5f1c114035a973b420a84b620af886c6bb85415071b03e7337711b9a031daad8266fd8479145f1d6e273d0bba4739b60909ec99fea9ad42e922a8ea4c501dda060b23eaa23afe44c26e46ca6d0ab8fd459245c9d0dc612e87c71f8c13222fd0cb968ecc863e44720a745879a94ff063563ff69d693ebadb1afd52de97c89540fa53b0a2e8f8585231f2847dc6d161ee03bad1a2ad9f86b03774ae256fa94bf2c7f095a0c37ce40c2706ebdbe8d849fc2b01d1ffae18099a9f1f4b0ec8c66a97877c20ef2895305bba4ca4eaf3754f21d72113fccb7b2469e9ab70b6c475a4c15db32f4975b92a3c753b624405359f9ada4050a72bd2222643e830032c2f522e9652a87b86f4a5b637049d2b616b915e05f102628a1cba6da5d81d1c7336fb23c20a7ed205fd3a1edfd4d579d1097be03fbeac8489208b06626b65b9ba63ab12b84a3d91a962c859168c994e3d59c8929673862ac662b9c1b02fc6b0c4bb7693c96f9c64c166f9a4151c67615a3801c22c3e7658e9723e15fc4caf1eb82d32756cd7396eee8e9dc6b0b5c67e1ffc03d074e93ce08ac5f8fcb71d2987bba4c19f92958a17a5d0bd805b735004d9892f6ad3467c8e45ce62e88d157f691733670ac927f9b35a9c8cb4f28c75bc416de9812b642bcfcbc5e4fbc99b10c04cbf524431554bcc9794cf789842007421a7fd38510a4b966dfa453e0a51b91d256ffa35e1975bc44363c189a6cd7836e39e405e37f8e593c987311c20ac0817b8cfbe88b101df9de4f9ab2b56e3ca4b3cb484c2b219db3badd2a99363418719f7ad8709f7eeff91912ddb1b0030ee11ca20f9657e8c8a62f3c9901cb2f33b01546451f2a137c7ed1a115ec708ad0194071adb8c94a9110e49e1b230ebd5eb9568cebd14326998bfa25bd2b0cec31f269dbf64bb885b838684f7d22c31da6369370d1891337de1050a99f2fd57be39c31b54ad95ae56b2d1ac8341dd8229abf3374691e73b61d3f0055e469d2fe4436657f05408260589c9ea02db2e5cd69b32fd82521ece16140aba32333ef48f091b2b0386d892b571c1991fba20e8053014b8bdd44d9cc48af800b50c9454d02d09b3b304d50d7383314250f41e06c1d91246d65d9e3e57d00db1bd5849a4f2eb576146902ba14b07b279535dd8ef4d14eb1900ed3de95975e1447566cd9b58ef3d079743c8fb356bf6f6571c630219f6b8558bd3e11c925f1819a1f33df1dede05b30511421373c39b66cab1a09d2d864a5fc53a4aae346a315f1c5acd6ed289dbfab029c96ace62704c2ad9b879fc171e4d9b5c8f3fe0e61047e26a19c48b92f918e41a53a50256ec551d0c4ba746ffc4f9c57e2e59a9f52dcb3de5af6498f85200cb9ebfd4b94d647b7e6bb25729e5d82915f9bb9f0753140e7e9b2bc4b11cbded1896c819e8322b6c65ddd444aaa14ea06bcb247319663a3e189c391e74ff6f52189d228128eff9b5890bbcde0d2bf1c542658d34237c2a483e45903e66c0fe08ca70319455ff671d353028a18945049a7f9b0263bddff2dc097ae236303594f3540945f5ce0b469c7c48fb4d29e28614d82833928af4c562904ef13a58c1cd8d95b00818f4c15eaa1329dbd7f240451b311b82b5c0a351019efe8f7c49024401ce272360f38a4979fdc0fa7fa07fc5c31b7f3d0b4cadf82d536df0810541b7850f3148d3670b109a78b14ae0b305698947c6294fe7418ad2f920e63ca6ba63010e8a10570b89fbf097c00debe9dec1d06f37bcc35a4987045b2295053e7337cae3fca0949e2e814031c7b61b8bd510599d03a1942bac721c137378172d2ae87d360605e3119bef294c8173f089d0fa6009a5a44daf09efa3d2240c652534662b6bd4f6e32b5156f670ee7e0bbd08f87f91af4606f22d2918a732009e7c53483737ab713df924fc20403776488635cffed51536ca535f06fe7e385964a6888f98aada938d3c2f0cbd60f071259e6da58a14e51244bb3eee01867af3f2d37a88a48400f621ac2e57b807cce4253d211f11590a53d83633e320d40ec01c360d313a3a2ca3873b98093fe05b3f50218c6da19eaa1926bb8bb424c0c406291f0bd5a93fb0e70073e6a6efdfab1106f969d565e518d779512fcc05c345b5b2e9ade38a5c7df2ed9c50c222015dddc0b68ece703c7e7b374c4e0d5767fe12b597613be4c76512a1408f9a2da38e289f69526e7cfff6b7634df175d380c498603059358061a72077fd1cbcb8778c54715283cb2ded1231b8f973dea8c1649e1a05e211a55aa03195342862ab1375c547a95813490a0288f6c60c4e97b254965219e1efd1b8b2cbb60e6c24c621085b5447147f52602d4074eb25be4062a6a6254c3ba0f26f9af436cf6128f6ab56cb704bff1b26354513d60ab31844088c969798c139c9612545fc1b58a10669c10bc38f0fa6702de3e332ac2f8b296d15966589e91dc2a4bcaaa24d61adccf04da16d82050c8669c9311387ebb43da2558f8eeda590f60f82c757d88a26c095fb97b6729a453caa5c7d1a6e583d6cfaaa83699c518bd638423b0ce8039bbc4000984cd35324724f67c98a73087b07513d3c0f7b45199a0458289161517ddfaadb2f0460e9e475787b2356e20b250cc9693c3cfd736220dcb7a45b12cd5768369c8f08dfdb8b359f93b484c377f0720e1ef1370712a3ab8b02a1864b1a99b23fd991fae1527643b21671a6e0e022489ae2069084b90309af9f500806f29af0e983c6236864186edc005702bd45c589ada9c47c97151a6619b77cb231766db850f1eb32d992720d2864b26f933b3ed0b66119a9c759bbbf149e771c387ba039f1b0050f99b2e825d3fdc49d6d316be8f22527bc5baf517eca52c0a106d533892e1287e7cc2f9fbc036f032e05fe0331871c67a5975982e41f21eadea1050093d476a19c0f740a344903eaf561452fc0c9809b2d311b62c500b1d94bf1affaa118c37a5b60c0df1e7b188a85e17a60c635f6c63561dbea57eaf2ffdcd39ca2f5a9b452a1f36bc88db25b0dbf04b345bf3ba7f44107e2bd9eb75791856d6736ac4e9478ceee8e4711a588227e527c14d1e3db4d7a5afd804454b1ba27ac9cd9f9b7cf6e9f2a5564bc1342fe399a9e7865d31060b76edaa54bb76874c31f5295ffc658d3d00d332931af28957daafabec565ace8062cb9d064e8cb8755724f6588189134788349e7f88f502a5179eb2b2adcbb6c86c17eedeca67f15df801b43ae475e0f9cd80c9e8d3e3bd2c7ad3c4f29e196a8a4dc5c313820662bcc29d45bf2893fedca288cbcc3209e24780a47f2aa38cbc05c7dc7e0a52e07f96e8fa6498eb4123a94ce506aaeaff98d3516052c0da704ddc3a18b81dcaa07871e0631094229adb3509eaeb3374c0e1ee659cb6dcaf53a1677c61b31587c491f1515eb5f95a0ddaa22d5d50a956d7afca2614d2d4a9f164f677ea266f0673398393fe71d8e7f276a840de47a87c87b800d1c7ad0abb6dd1464cf61e2cb98400459d0b2cdf15a0d75621eed2d5d31b4226ce0132b7e63853829f09023653ff70d5000d8d20e0a1646b4389fdd1a97f84357c1767f516e44aeda64ddf1b20859711e6d22638e6f4b80b96328bd3a4798d1d773e924cf7ab1517dec0301b0a1f31b69b27020790f650434f26233f5d4e8f1e588bcac09761bc0e7e67063796c9c8de9cca8a605404f1d9e6f0aae00fc72cac1e1337fc6b11ac4fba0d34e00ae1ff5e57bbebfeccec88eacd3041321487bd28d366e3c757c24d747591eacbf570d6d9fc8a522d820e4b7fec63593d1cf4f4fd2eae8799880d160c553802b6eff3e11811f13280d839077dc20139e1ba2c87908f3e5218fc441c6d3d4afe17229f7bf5c4cc96d521e2f7d7659726a1d4aaaaace06697e930f15bba285e1462ef1d1e5ac72f9c3d06dbf6673a9c252a699825992e4a98d6c223e22b0c4281556c44d5c4d94b847e5b68a5d235007891bd9dc16afda97d03999c4e69bad1f602f8a893323f661060659b2b92acde797ec579f932500fcb90f6ec9d03af50ab9e330e718201a60ca174be795e4e11793d9ffc66023a3f464046041629ca7f204ab60f881496bc0e07a6ef17f44199bb06d7a5901d2a8a5d97139794694bf4159bb9712630a1ae32bf64a995f3901aba0caa9ba822abb9eefd04654c1ac05a98d89911dbee44a7df2c903bf720eb7c237d251926d2462931488cdf255ae74819fea9c4c9a5db9057e7ef7cb552483e251c1ca7566d052e990e7501560af69b2e64c0a67f93fda0c33bf41c8377c669d580f8df11873dbd96b73e4941feb115fc8ffa399fd2974a80b16dc7c7efc9c1f9404c5df19f6cbad024ea7b5c27999a18250aa620336a8d6287673ec639c6593da56df02585f01953e4c250684b7f7eb22f4bc0197059d662ea0a2f446ccd4bd4cb7115024d019adf25db7215690c84b91de3b2de55e8b81e5f640f524bd46ac2d7620ce369e08f40f03b103a1a78f048252968835dd9688211d9df85d69bdb8480ffcadfc584fc9929a75cad66e1a45b45a9e4f103e54a53136b52ce8143e293d315b30e0ec88a482c1aa0f5d91ca1f8ca1adaf88ce7c1ade7e78bec50594d85018ef9b29fa5b43cda06c518b241bef7662f0bf0d688213f1a0002acc15b90f6bf244252787000d100f5bcfe9cfbfb0ca04fe0e2b28282ecc35a5263ea47826988a9bd0c7840c77cc0094eb9cdb42f54adceaa52cae5e71068437c1d823d08b7c334115a8662b19210df4d6236b8a76edce508528581e3e459226a8345b810f5a6767feb94ba3425b7fef2f9849f10a29d38112ed0a7f7319cf344295e2d15ad6d0bc5df6f5cbbd9a15efd6a2d2cdf1c0914268c0696276950a845a68dbbaa0ad073ecf18bc82a513bbb41f75f85da2333a31234c6fdc238e0e2333026bfde4f878d0e2a959c8ad1a41adfba34505f9f8180006312c9c76c2f3145cb6a2edb105f576b4d00a8e680e60974204913398b16ff623072a8d74680e506c4c2ce0823a1cf0983073a6a1d79b5aa828b5d46941c6da3becd12b526e05ea03b257694b29370f9c4242c3eba58c2bea350bd096d36ed1bd18814fe8f9d708730f5a95a1b4534c848942b0d5afe6b0e4c3b4e240ab3820535426d9cc91fa768c2072cef6c2e3974876049e5652404a01b1a30d1b6ea10c1b3565676423a35e556e6ff67c48973a8dce64ea2f2b0a85c1a5764d5a133cbef4829499a31eca986c23f747976ae4885d68321a1a3efeb3cf837ca75b80362d44c2c24b35e3027233dfcedc7886454f878c263bbadb9a6b2d14a509726de03d9d5e2697ea29baa90eba97e1338057277c1a47fc9967783e50b9919457701d983f5d975d7e9e96625af4b363b0a105f98a46c7c4d1462e96c512b6eba705b534a2be0072c5d4b79838bc1d00462321f73393a69e9229ad579b0ebd94e684dd1cd17285a22589bac464279606939f7163ff84f23d89872501de8e384c756a870ea921257283441a9d66d73aea27d40b36c175999aa7b89109799e34d37b00343075191c07e2bd0666a4f048aa147176cef496bc217b49e6418e7873d625db27b5024019e62d2814a9b938b38fbec75ac90f9eeb594e114886ef54db031ac8dfbd139a44be78c833edf67f49945641a7c5688b3f7fbb3ccb2d85defd264ff4ff17ea174d4542daf1ba18c8a2662bc24584b6c2318d652c6add87d6838581abad6849d76e161c491484746c1b2af16555e84f043329329ba0ccaca0b9e6318c0e6ecb7f2f1021570a0af49797be983b3e3939d15d612d1f37643be01e2e37a680b69d2f5cef2fa318d206a073ea5490152db827edfb0c728a9ebdb347ba44c5840f4ad2e373b38013fc53030fc7a1070af8ac893f75c50f4030c42842ae213c27d105355b728aca1541e7a1c4e71de0d773b0e5e577fceb291a9c5fdd13a648b323a7720ba895615b43039aa26a6513ad95060f4d115a55ed11a16669c9fc3321008f022cb2214eb7aabcf0bfdd4f8ab67767fa2cae7c8ad368437280187fd37a148d912fe7a4cfcf835a155e430bd5c1d05514617f1ee864dcda9a7ffc47458b04114c125219db72998c0675a001a0e7801b082a90439a38b28252500aa5009add04c98ec9723a858323922bd4f7f02e640fb59086a4b585f90095a46a5fbfd690ac1e09926d14df4d8a35e07c945a7e559ee97de1012d9013d9e94c743ca5cc1fc0036cac060e6f0955a573d8303803d6b952187c899183d23bd44afc00b92b3364c589d81d13d3a28441ce211d4ebeecc5a43b7c1666df2ee0cde45eee28e190eae9c42103d995eecf7238a4e20ca760099fc690ced8838d7c1ff59c3ef8e2cb5eb6c2c90950d5051b5f74bdf1ac58ae0fe9ec34310099221c9ebc793e31e47bd9bc6386ae60bb05351478c1a8ec157fcbdd3f3c81dfcc2270f977b18173a41933e95c34d82883aad2988b34a714aba1d7b68ffc06bc4ef7e0b6f4945d60d912df7627481a75fa874210accdad02613d9bb66e42a12901484a650a638442f3b21de652237c435afe5ac25ecb0d0cf8c155ffed204618e280bbb83602da1abb7c709c7a7bf2a0efe72c5934f1af54cae1f127fd9584ae7eb1c004462469912a9d2ded4aeebf4f8752721e2dafb70f27b6ec0d93f2c9535651f4ceb9a2784199ba86b7237fc076169d9a1bc8ca371087dca46c41f54aa14f2981617350ea4c70a43332683b77a5a3da63b73b5c04fd0c3ae8edd19bd50e42fdefc61ff6eea3f1917774924785af02e88bf11ef566e1c7121722752dbe482e9c5e7e6a872d3acf65dffd6817fe6bc80f858c981425fb2c9b42429cff48cf9521d2e6241a4ca479dd8fdcc32b2c09158b842b1a26a701c0012a00d876fdd1bfa137be9927a907283e912ff7376406568d22bd929d1627393c0c825a40210e0589a560e9eab6c10e980a95d441eccd6c8201a4d90a0a1ceccb5705f16e295e2a20ec0d04eca2edbd549ddb5f711ee1de786796459f7de8eedf01a8c999c4979dd0f97a94e6693b0f9f1130d0716282a384a6beca988f32221479802e0550c75e91fe730290f909f2c6056df2d95976ccfe592e5e3ec5ed34c73013f835b36b4c07bc39390fb2e52f3fc47c9ba14b717130710b7ad9d5bfd91d0120225b9fb5db37129e16f09097c4ff77a95a8f52accce80f5b57bb09502c8cf1c1663abde4eb168c923d076295d75236741e86f4cabc6eb2bcb0a91718f800365ffca290b41834cbff960d8fd81a2855880bdc201297cec2a75faa18257a6db3b82987b58f1296203d78e19c89cbf8d5d487247ff2154274b131dbb060511837553a245c7ca7e94418c5edfaf6c45ace0f9597218a4ec516ff9a04b5859e09babd84f837b635f4784a796f89318c40adf3833b7328ec4bccc354d9d55506c4e8081c87a98f10ea492bff299961d04a6d42761849601f48407c07c1ca4ea6c4fe8804564e2cc2da04745288728e4ba1a839c169ff2ebe787280e0e967c909bc8c03ec9f0903459ad21169210e60afdcafb436a9bb5f7c7eb1746afd9ca4187111754135d51d398e438109a97191bda76a880818c68b801adba4ee9d45b7c0d16cc2bbbef10348c902edcd5159438a3790d68dbc2a4214108b6d0b04672eaa4e045e4165e60b12887a8bea0ba2f21a4d08f3beea31ee02e01714163fba8386cef3be99feca2dc3655240dcb09796e5da7eed7e4cd1055977c115e9d21dfd95040b89f753e0b85be2dbdd5e1de3c0339fc58474c6baca5f265c5ca92cf9ceac076e32610223280778817f0bc681a2ec903fd17e4146b329c50134412f8f49f54ddaeaee7a598cd08f9450dfac81deb463715a8abbe2297a446bc77dce0b0fb1039f79efc0ddc8acbe2807aa8bd524650f7f9d5cd0c6707ea5edb3ab6e5c3fc103c87172c1e5e16bef0643e73af523ecf12cc70ebcb76c63749c8cace408b74620f5c9c6d1d6807369a89e1cda6ebd8186fc6e0029c28a59d086373e00678dfc456b0c410bb9bff42ae1c6dfab45f691f19395001c47bfee1f3c4a96211ca1b11617ef9d0d3dbe0e773ba412cc42aa21248037406ebc6492d824f99390c36b43bf561d23f0b6bf808ac48c0e0fe93f281c382c180d38f091c98352e26378ec5cd1a82b1762d17a50ee2b15f201ea2abeee659c3cb3fc58d51c177a3adc9d4b438edc2e9e5e9c998b192295a6ca5c1065fb4767ba8d66bb95b5be762eec4673ac0e0ec265e09a57032e4034b15b43a26be53acc429b64e8f42b55fafb88b6424c9ae7aced63e4a7d058903e07f406537a22cc0a951ca90f658756b5e5fc6a972409c6c108808b090487718c19da37c6ff60fe854bebfc0945ba1825c06e8a2c0cf9ad7cd8a3620d26c22e88233d35e0725d2adc228506e29e71f4f3582e1c6e1ec03ab1011c8b77b1575cbe4960d0dc08b7c590c73d3538b25dc0587ab723c7e216208541357d387c058f3e354cf6305ee97a8e6d215caa90ba49fbcb8c836d41d28b404663fabef506708bafb6e3090147a205ce2170814bbeaf0340c2528e2b90c0a91fa505aedf22831b1bd279ef04ba48fa2eb4a8868ebf6c74ed374ea5f31a17b61765d193c2cecc79ddb13971600e5298446d1d633641114db0366fa1842f9af55d26fa8bc1c0c77f23d02ab7095312326b660eae8a433a622da2199948d9e1073e61c558ba5f01eee817e1dae4486f4bf917a1b83ba65db77566e8733a2d32afefed93c71f7715b5484a2ff315f93babce640cfb10b343cb19e1cad5f97b37401d11fb204df467eb76dc04246080e241609d72a08e05f1bb21e89676155bcb432735fc14414522893ce28f9b9197cb938c71d1fe52e0eb8ea45bcea1bd3c27845d3a9eac1709684483859227969c1c839ee918e0bb3f915586a3ac14354b408ab4688491f79b59be2fb96268e719e27bb60700cb76b8bdbd46b28a4ce75b2ac7f5c3b8c1f9f489a791873bd0207c3fa7abdb55dedca43831ae7e99e1dc12d2aafe51e51cd3e98f504bc960bbb075d7831777ee20120f89965cc630a0d80f0b59dcf410dc4ad8f2d0b26d0f83b080d04d049ef18001ef7892742915151b11774a22d809652ba051c119b63a996dce93d0402f02a30c0670b40fe760404cd96c4a7b4cbd710f05cb429280269ce6910c3a6d05802b7819291584e8dba94e4237255991d29e37dfb26d811a54aba3dc2fe22adf1d0b7d8c017600684a29b612209283f48b904eee4df4b4576df521b267b0d6dba3c918d20212a83da454569bbafca8819f8320a60f1a63e6dee47982380a2da0600d85e0436e37dc4187c76d3d1e4cf6231f5284fec560333da6cfc02895954e085d43798cd51850d479d09414ae4f4d43c43c7a221b82e71faebcb9c9759c5945285e6e14f7ba43f06a136edaa15efaef059e0c91c36c6542ece8af3837e7197fec4b8403fa61175cf48fcb8a82f9edc0fb15eed71177afda0cad4383708b38f42bf7f390f93161f6814e52d98abf2a3c2ca5552c8915a8b05b46ff1d04ecd5c9bf264c9b4a37ff2f6c84fedd443da6c7c7eb92c2264ae7d8391fe4fa5bf2013fbd961d196ab495c7ee144901b01635c6360de04db600f7d28dcba2ca01ec3e5384e31c24d3681d4fb284b1c1aa97f10b6f68792f134d7c4850960000e031610462fbfb709a70aae77024a5c608318d4c753078d2d81465be9d01dd2cdacff8ac48200f3fe1de830550045f1310d362a3bca4da51b6b13affc2cd425e9c064ed6e6dd6d314383d0198b76cf8c4e8a412d1ec180c3b719a246ba9a383a31027f2bba2663c02b93731b446208e14351339b225243a3770e0adf8082739699bc43210ec5662c7043421bc8c484518a2005f020b50885a9e1fe33ed783ed74fcc486957535ef099e6531089118419f83041244ad877f8617bb7134ac76f6c1e6cc1feb68183b816853fed027902de71c430264a9e22659fb0d764321d08e27b0c964fe047415890752c1b3534f49955d899ee39ebccb67aaa3a8af5512c727a20e73e943bbf086740ff298072e8464ae57693cd8a216a2b2621bd835b6cfa4f2839c80873e5e951a3c2a4f4fe4fa6bcae62b5b4dc734efd0cff06f481bcb8e595061092d08f255987658173898fede807681f84a2ac8d607dde53e1ea4bfd5e337d7efe1c45a030e51e17a3d73e5b2104284887b56db065e51fe6715ceb424cc861c24ee50ae16091216a1002bf10923f64779e771650ec0c5d77022cedc2f062fd516220716dd296b9ba76e67d5b85449ef42a5cf5ba20ee8b135bd99af69854385341676b1d89d55dc2e5c74cba1187ebe25e52017b1eaa45dacb596ae8c72277b164804b7dc86a85b10fd323cde97c9a8912e69d4602770ee69290724f7b645a5a5c29451a458aa653a8f609bed9995ae4fe69232bc21e9c2a543a7ecefe3815cdb2793edd39343fda8505bc39fbff5742aceb492bfcbf52dfb4ff2bbf927ca50b38efa5af882b7bba76c83c9997d3b19353c373fefafcf7ab5cf4721a6869605fca576dcdb29ab4c76a05c2521960f58b3c44313e109969a44596e757a8c2995874dbaf3f22f72e3c0cd871fea973dfd4f990e8012f44c5c3b0234fc8a84d32496124673426e2b5ed98795b43b577b9b6510e8b5b9b8d93dd641226241c6d82600e01d7d8fe01fc64e9a3334b608488bbf8c2a4a7239e35127f3dda5a9792574a669ffa73a706b5292b2f0c27a17336b0044677328549a74b33aadee672cee3fcb45ae84f2ada1610f3d7329c985afb03c170e0b17a68f1d07c582f05dae2393e9030eb349f0061b29753a039c9077a1c54c20f0f0fbb6560c7bcc82012d789206ba62645cd4855ef6294ad7fe5e18469f2228df149a00a5e60cb7836d32256143884af05f7168ea61121d9fa858c8fa1aac9037ce8e365bcc0029b20f9b42f6ad90bf666e8d11ba99aa83c7ecc2a5c4f0ddd3369eacf2c0550236c1ba170d7e422245514f19a8b1ebf8682fc1e62209bdb27efe6994f6a18ff27990ed420408b0f483d5b1270ab7b641ed662de19eef79befc3ed86f9f872733ebcdc9c0f2ebe316ff1ecf20271080593264476141d53b526e22a2a594c291be3d937d4513247d92ba646623fb28c7932d7f2a52b82abb16cb3002657d617bb035cac8905b454a98cef9390e6639d8645fa6b595463b642b9642e1bdee69746736fb740eeb16378d9db464d462ba1345aff928954c1ef04b7578e5cbef2da90570fd4dfab9ce076b007311bf37fc367fc86ce17494254c1c1c6279c38b86d5ce3ca516923d3dd99d8c2f638b16ca324862fe08dcc44ae3e5de22e11e8aa69e57754c022e28f012f0b5e2d8f1137dac298ad551d0f1dd0b2ba2aa84e611a3240726450fb8232a15a6c91637bca17274d471a1da645b7e5334b38f00e99d83d710988bc4948b6d4638109f1525469791c5524a124b3fa4cf0ba648926ebe4b177f8e6aa5c8a852f5e69f6dbc4f2499f53957d2cb6a23d4f30a36a752111c7545c41a9cf3f6e6f94a0e5337c00f0c0269aa1993d6a6831e26a905678f4229de74a43e3bc6015ba63f845308f6422b9f8f26838140b342183e40e87d17f875cab1f5b1731802edcd2e7a3b26b27ceb11a1239a767880f0b486e455b83a59bc5928cdf42d87589c90f63b160bb7460f330cce2af6e00aa2362b03f26de868318adb405a942df57086973e6620b48ca294e7ce712f98c3f4402f9b7305dda0c1928fba91261574d7cb6f7bb502e3f62bf6b55bb8a7f13e4a8576ab88fa36b87c59fa8e29730bff89f4f563f5d5a444f13f48a28fe1b36983804b02ed14acc9f2b9931ded1299bb47320e8c1d64cd9f15909fd98a5ea7ebbfa38dfe845ad0117cbbc8c2c932ed07f5c416facb2f4cb38f893e58a3f0de4dc0be4ab446be4033d1697a89beb5be983b8d6fb7a7b51c3139d0c2763d5c29a340479b1a9236a3f4b6c526ccc6521d78a6fa07d4b42c8c42ed1b4d58b9c5c84a6badedaeb65282e9506afa24e7d01652dd7d73cfc35b5189c7eafe98b55bfc97cb1224fe830513201a6e562804f609fca4deee154426e50405a86aaa2f00d7c2dcb76993434a4b54265400adc0da9669a85c98206e869248e87cc25931c6deeeda994c9effbfb9c45903bcbc5f75e3a86a45a4c7f425e9d4af374a85e36f3b61231c0d420bf2d04c6a428497bd2912f21e8164ba1ae8dcf58464c4d049392ac7f989ca134b4c8ca3b7555aa9c242220e18422c4db78cf60302ee88cd4a0dd57beee5c0638330808f28926f262af387fc2b088eaa70996b5d941bf80bb44b45ce4b567cd3f81680d7a8f6414e8db07e6133a4bbfdd464b19c3ed84015a63564dd90295d683def6bdc61f49123842e4823d2556644e4e09bfc5b97e9465748324098246482932b27a523dcf3957799df5d71a058bd708d8aae435ea1e45a82b8f80b694a95a41a3f47e62da48acf5208a10b92d7cf9ead0a34265fc4b013c7fee5f6ec70c7ba4bb94ae7dfd8cd07e06c411f81c0ab49728b7bee34d233fcba7a33e7602884997f1167843d45c79ee8ee780e9747b37a2a775f8ccb3e47e1661fc0ac75a404a3aca4eff6bc872986b63f817950dc9a96ce4fe8e34097a2427b4bcbaf7e80f13b11b4f3ae4e88b6e275f6c078f9e90274235bde105f2e91ce16e609e803aae0e8e42482ee1eab244c1c4a1c179773e935b3d712e1c88abd7cdfa5e1bfc7a44856754632fa8085ce91d9e3571ed7a9068fd1b7a0f218d5c602a15605ef918f6d0404fcab28161a51ca5d505899b3669f4ad2a4a2079da3148ebab6c5ad0ba5289dac1b3a0dd1d70ab9db8bf2d69daef3d0cead8b4058eb4d1d1c803f7ee71456de60b07113a27783c36440ab21e0b5b94cbae909ba12259e3638cfdc0644fca1ca0de0bd3f7ab8845a2ece89782e9901b407429fe79fe0615c79373c5fd7ade7c631f110b031fc5c9332fc6af0fee2b7bb56890f4159959463987ce52878b3970a32453430f7bc894affee31dacaad9d7a78cc07dfa5c07709e951812cc75ac6869ce5c4704b6b6aa5964c53c247b315993ed35ed66406dec7eb31db250cbe776e52fb38558925d5b195b9b3cb33930b7f3018a40917203763db6f7ad63aaa2109a21d3b791b4b899684fa1404860d531a886dfa72391e76c40724367319e59f05cbf4a6d78d4b195cc9e1d4274d3c6bd123b98cac064c5817db589368de54c4d7fc07bbbe63b64821a1700f3380b647d2615e771e16fe0014a27864ae02dbc2314380684f12b8731fb5b54ff481946c2d9a924614200910f90b8483cb34b00fe06df70f3fe9f2b92cfc0589248c02ad8f51f8011337b4abfaab246fc91cc358e82c32a1fba965438a45d91e5f7a5f047d5978805d873e171d1c841b34218ec8b06020b929ebbbec1f61bdd2da779484df01fb26b553536dd05ebcab1100d0857885cd3363f4755f910dd2dc192cb489ebc902a85431b55c9b8f18870c706586c077f41d67a5fc30cf19e432ae9cc1ab07211c2f9889e883a860311d2fe701511368772816a8f0d350c610e1400c4f1c4c5786aec72304c810786e81ce0e1de0cadd97e8b3f54538c57827d11b2af32834cb69b894f6cd9429cfb5002a264f7c11a6c48e80df02373b294b1219b1a9827314a9159f0bc8ab85862e89097d1891b214f58267ef01b6c8f35c0d10738c159230be0a78b7c90d8e6b05c645b631cd29781fe7068dfbd00134b3254b18797d4c2c4e9051857d9fb918abbc8398b6710f477b7816cf59be770c233a47c072107e27310358a82eb0c1b0fd1b428e510fbe37a9906b5cec154ebc07a40c07a12f8ab62a5e96241cb4c1f88ff2ea8d37ae1da956f25e7505f8d03778aee2e097e4d7de40247265966414fa6501f0f50c69a027e4c82507aa3450a9df1b2d250def1d0ee9b91e1304cde10633078f414f3e64ac41bd16a48aa61e920daabc2c839126e5a03e5c169cde08ab582a76321bd2f5ca6b74c7ec7ccbfbe2cc9f630bd8ccaeb31648804fd3898a49d1ed07c4870b31127aedab9c375026f2914f9471ccbf0f88f7e4f7c45c0651a8cb0480bd9cabc82d8b9590d1719c36070ae4747c3cbc57f1865677f61c2bc900580ecfdf6ce15002c59af8fd319035d8b44a007ef40720ec25d458c0ef75d1f8dead9515e0759f15716b5781b1b3ca9eecae5fe4442d9abf6d038f564044c84ea9b47968c01fa05836ae525630648c0986ed1d50f2120d46d8fabd683aefaeba65a68b791b6aea7bbc397a4cce34c95f1cc7bbdedcd498ebbcda17a30867c87683b36e712ce0ec7826bce66e28e4a3fcb30d0710e9470e614fae2be19f6ec17c0316e5fa8a28cf995edff4fb64baec9d9f4e9ac3cd570ba187718ef4102dac37ff11414b8bac19a84fb50ca43e52e040c71ec67a82b089f097417451b7de57efd746622e4cb63284cdf39510ad2d888cbc8d5599ae6e8a1d7a1d25c6b1a29c64d7bd9e9f465546279845b4a72aa9641b121d1a271a0855241e50d4badb7ddedd11a7e114b765592c3c740b9b549d852a4eb512ccded1dd65b1a62f6d8f00e0443bed2ef7355c26e91f55dca89d0b09227511c9c43c67663d905bf086c356c930584724318673256d0444295879c84d89f28ec24bee85342746c1e6ed21c60e24656497f531334558516a3cbc0eec8cb7b147c112fc240da2a60808444c7e3d96cde202f2842e48f2a7c5507950db503d92a44e449c4a21fa0196ffbbb99df1990f0821125396f58bc957fa7e2100efe8ed5a088fdfd973d60dd186093d7d4ebe3e64049faf238a8d331134dc5c34ae5757556305c39fab5f979282b04c1499f3bf2325ea2da6c74d8ab18337b32ca464ac7a46f732e723a73f17765e81ad318ff4b3b4934f5c0318200843e4e2290d62dadd8fba5cb7c7cc139e6b806ce3fb0d15048db9781f8431ac2006b0c43942321d2bf9195e021b76587551683bded0baab9966e1e3a23293a8cbaf00c77f666fbbbb414ef234e711259f1db83f0f5970750bbb66b33ae505c1f95bfe1e69f3a38712e61a0e0f879649a0b52c88bb8ee3f537ebf31b058bd161437e57c94eb5c089fd96d5a89a0e69922f891eaceea6edab8e7c1009f945446f6284849019e411b94a8f2f6c325a448407c58d69c69c02b39d156ce67cad5f478bb0f747eea6179989d552ae972c32fd7b7fe133d0d788fc044931f5b2029f2409e9407631244388b885b4fe81f97098b37c14f5e4e1db0299314efd2cc58ade28a8e280aaa8094611360df81914217c8357f03a8cafb9e90d427b138210e73d6bd0f248d3f23e4d6185e13096082159bba9a935c17b4585f9ef5b125dff8a57e353ba83204dedaeff55d2dfa7702d68349032bfb9f1f7cc8e36b3cb486c4efde6e6286ea686bd1fba0fa0dc747b1bf0e1c992d7048effb557e0157b28af8ba920f462e25767d984474d5a2b62f5376363d40fec8329c442dba87fe21868999677ad6af85c002d8a9ff78c6a064d425514e5f62032e8f223c9897a6a7f0aa9bd115d8c5002012359f25f831baab4eca06748e5cc1241ce99c6d6b233d9a2b278dacb5a041bb5e379e47392da44fb1e2b45e4b930d968abeac38783b39bf2bb27a422888ebb778d1dcec9912c71f9701692722baf4a611ca24e069c2ab30e7ad1026b515ba3c7df8905044da273c07a661e949e6db95f21ba355050009d2adc6d5761f18d806566f49f4de5558c85204f6d2622f7340b79bc155f1eb407b05cb95efaeb3ea028e3aae015a9e067e941fa80cf135cdc28ce3a544be6782c21dc134f15b1623231eb7876a70f52e71c03120c67555410e2b2867ed09e7a5d4e4476ab76255fa79618eab6f112b9be95c5c9b39ee38f6413eede1b7e5ae3cd8ecd1598697510526551c76345e637a08dea75a7f5c001cd802adf3ac57f4ec012218184581319a7e85cdafd602555c339a6dc639f1454c9cba8f3425e70d6264cd31282010599c0a20aa2e37f4d16ba02c209dcc58727f6a74bf7d493ddd5a0349c1808b52daa39c9f5e65a04d8543bbb3c85dcea17982e741aee1bd9a7bae7c111e490ac37a8ff2b9221aebf7a4acc87120897d69faaac2e9333bb13fe7a436475cd0a41170d52dbbfb83fcd5c9857039b8d4605b372c59ffb01f8b95f3a1fa85bffe1a8b460bce6cf4a2b1e74feafc5d21e1baf61f0f1880f4edcb08b198f43f756950469cdff627db6c9147158a4d89a270d9788bc79debcb70a963bbc22cb4c32146a2126d34f48952e26a7c4646edeb11737a3d2cc1493ae6e83089c79b41ee35a8cf9e5cf485827d18473fbc566cf73c050690f05bf25439726a86b73c9bcb481bb8d9fef0ff78cefb1ff6a0b55dedba125c0ffaac43d9301ce07810015058fe46946a203e0506983f299fe7561769ce5eec80afe624fde66ec2928427a3b508936de1addae73b3e2232aa6d3eccc5b65d75eb3e2b391b4b720c2699c59e7200dcff3642a92d2bb737204ac7941defd91cf56702494a4e0bb41d4445b554a96764836b79016462011eea2253139702ee70714482616a6ee14c8bb5f58e21d225095d968a39256606cd66b860060ef471b496bc56ef997b85288ffaed7c61900b9d8d220fc3377434fa9920d2a855526e2afa92eb85fdf78892eb976d145decbc59b011987264a51273041cda9d9023b22745ccd34a600179b222e22d5e9fcd3f7e4940b20f56e7bc4a15658db069ecc6cc09ff4316b56e01280384ffc260f661849cd15bc2e43240e26f63dc8a61ae186a950cd0b5191b2ab1c10b36f93145c022c5fc3c8b0aa51dd6e2911837bcd4554159e3c83c9b8fdd60a9da70987a9bf3388e6a531d51821bcd66f5df8e5a331c80772002d64a2eff042f82481e25c91a48bf1d6c0f6207a25410f844a9e12f36d625e9ac3e015d609e74e5e9036ef41a66b2d4b5da49602c58ca359600c8b75b49c92915500df61b5bea557afc4019c11a6308133533c7e77d0e7db7ede19ff38dd50223ee977b69d001a641116c8050902fa558809169acb1608a4fada9949db8fb6c5ec09aa183157c2658e30a49d6ff670a50d378ca01acf5c2d9470f8ebdd6d3e8ba0c7309b98776ff9561ae28c9948e77ec6e90bb92b9216acd22e38f1755663d88bdbf3e653258656b7cc4b79c1355d3f5ade82ca2938bac8627d81329c91b7e05e72e8dfea43b57aaee4882bec1e62b8a5b56cbc65a283ddd5b09e67aeadf1e584ec6fe6f420d845a4fba72d7e2215551ac84b3849f03967cc1bd14b7aab55f2b82958457f3491b17cb0e68b4ecedc2fb7a1765d540551d13ad91c57adb267777cefdd1af3e8f29cfe94459372384fb3380d3b9910fbdfea4b771ee8e6060f500a20997b904cd68827868a29e33ff4bd84dc6318bb8fa0ab844070e577bc9af81694f0ccb7b44b3e40d0ff6df5bcb53e038debbb79bb920e1e2f7d4cf6cfad1793c9ea2ccfaded8ce80da13f99b8e2883028a840df08595442dfb3e3c0ec9f1021638b00c0cb2487e6ef3c18b51fe1ab28a0aa400b0add28c8744f5a34cda947bb1ae26309aab08947b9185894155aba9a9388858d38114df15fd66f48114f04134316e60a27c3c908ea3fa60cd386212f41ce1b1809808b00627dfe9490e9f168a62ad44dd9ad1518dd866ecfc89d3be0a0222f6a669dcf36d63c0439d752032ca13672d773cd7f41106125580a8ede918eb21a1dd15ee878a33f79980ceab4006e00d0b42e141a0104c118016a7de90ac10df954ff40a60cc65e211314bd11bd1d788f09184007a2f979e24d674762bbe2affa53a4c1d8c6a4c1578c4b2402bf0a422e2d45c9219b93c67b752c5a6f4821fbc5efcc13e2c1c7171406704f62ae547ab3940988fc69839d8dbbb59a939d89b146b1878705005bb685902412bc7630f6d88db7579aa2bed7629bd6c39c9efd74e1bfdc5c0f8016f3abf982d67afb72104bc280ef5cbdd16407908042ca3e5aecf531a012cf032242081c543f493c2e888477079dd3fa3e105c4b9d765183f410d471b92afc70e64cc850b6432793051b40a513827e6a010a1f4ef56000e6b43f43aa660616c904053a04efcc062e1f6f55fd1c38509c238dd1514206478227d077f01ecb46a004aadfd79d8bef133ee8b5c713f262fa546316bd981cd69de2b75dfd3b754ce3130d58b7566950600e0185d339ff0243d5c82bd393ca096b30bd451f544a3ce98087ab61feb0fe99fb4dd9274ec59ebc92de760c9250533c97ca0764bc0e1fd47fd13d8e8ae6541945a3059bfc3216f73b553bf4632df5d38426d2ee21a6fed78f7c518281f6804d7d9d78a44e06ddebec1a27b9875ce5e5c694eafd4d92553a00522d01f3d014cdff52599d049c53e021434020a091e7256208a526e0e99fc332019a40ffecc08eed5212f9f5caf0e864802f6b367adc680d45530afa95ff00f7213244dd4fedea29735fcf1bc80be431c6ee06359fdef1f6de5abd0c9bd625bd98e2ae7e98aa3979696966c29530407cc07bd06378932390fc308cfa343ae830c706da0c815004766882949727572624491c34990859a1c0374e068c88db8207c8d106e1526f7e44d07ee927b04cb1c1c554e4d6e0144b8c91100f7c25d2898c8391b28aa1c02705bdc277f628b9e1c6e157e759988e363e4ee1730bccd85e267dcf0592207430e090dfae426c06d20a744aee84c89a3e0f670c4009c4f550438772786b8c34709104e94e440e78e91075698a2e42a8b1773e05c59dd3e37cbfdc10b2a36e863e68ed4132839578cbe39fafb961d2cf4571a7ded13304f3ec8f1e2c20f570c1cd709f10152725ca8d143a60731e4e499e175d0d4d064ea861ae6b401dd0a1e46d6f78d3e862cff5efce20c11c291b2a385866b267e9fab05a43d0fa8bb3326d7441f3d390fc78599a9dc1a19b84b850348727b9c0a3a570acee9ecb96cd0c029ebe04a3add8bba9c81e4ef7ef3845de7799ee7795fd7b966ed77aff0eb5ae90002ce91ee007f0cbdf2bf153ed275c116100e454d732b1d7a2d1d82ae7189fcfdece5fdadba735cf7cdf7a674104b065fd4dd97b6b04b9e7979b44cb9ca73e55145751e656647d46b87a765249367de1426ba7b0f0bce83abf0ab7b3c07bf70dd5567e53d7991f7f7032f08766ca2cc8c99755330951046835f6b1c47f7a2103633a6d381ad74e974e0249d8d295da2b4ec98f27af2dd774f38c715ce31e2ba247a1493c3ef92e82ec46fbe63827360176e835fdd77df3dc139461766835f18d74de9a61cd1dd7756380708d58126e8e8ee4b1df2ccada67455d21d9a29fcf1bbbf91bbd7c08bcaef3a2ce3c30f47a1f0658862f8a0d0c8846705e748dd4a33616f4a159c63fcee4bec43f40074d17c14c23f8ec93284bcd4adf48f6b5c228f3ff384f882738c2e2d3c785d700ed075e7dcaa4b75d75c366d1c479e6b75af2e95be9865855f7930c61813691e9dd5bd5a4335e7cd5826cfee1b2c63b6ea0fc7ba33de10f1d24a5f7fb75d3c25c66bf40dbb230fbd68f6ca1dc6186949b9a365f32290c729a38142441cc79d3d51670393203800488b478ff1c367099df8953fb86c581f0a8163904e93e3b34a21da0e4dd246978ce8856dccf8b1891c868fc2117e5d0a601b178810080e88f2bee83c2f0c3f9a0cbfbc2ff4c047e108bfbcffbceeb1d841750f8a1d49de1b86611886cf0313899e8ef2c3ff11f23ef46c7836bc0ecc23eeae943e34bb6c527ff38561188661e86fc671fcef297822873f732bd2351bc7ff5efcee78b19df2de7bef05efbdf7de7befbde0fd527636fcfa64f8f5b9bbcb684740fca9d0f89823fcfa5000d286da7b2c24d44cf8af788558dfed80c0b995169dc8b38bd314e056f88e6543be382832f922cb863cceb2169fb4504ed0cafe0e02604882562b82a35214813428dd451a3ea248a9aaa2af1e89c8adbb46df5b25c5835439c1f8de7befbd33c0291964a24a86051a7f34f2775ecec0fed1a8f1fdbda3af9eebafaec35440717f78bafb1990aef1d5f85cebd55d96fb6e1890bf3a5a8cbe1b5779d6c2d75fad1ae07a83700ecf95e57690e48deee37fe10b3466b158f88583f00b8f5af27f42ebd59e97e53e0fdc81ddbd3f45bb149d367a36433b9d3c3ac8eda3af0b82ec3cca07f213972ec8eda3854a1ad9bd32e7272eb8557a8dfb598eb260d78c9c817b759432b27be50844fe1983c82c0fae2e3399dd0b4721f2989fb8742fbe9920fad3f97229c5fcc445c41be0cf71abe070e785d05d3a25c806e708435628f4e38543c0c0a7bbff8b2fb8ae11bc1e897f44087f58e0f5ce0d8b855f9d963c63b132502b08cc2c949f979fb8b80ceff3fefbbe4f1473c6ae2ed7005da2ce7a268e54b2a7c50aba74308de7fd0afab62610c76f46a7af2b040f1e3f16018760019766f21e7718cb70c731fbf1d1b9b20b0a1649dd913e7b3d2349b7d1bdd7b9d22e55c7083dbba1bb876997aa5346fbcf0758938ea3cbf8355ac9b31bddf7254e7b42ebd518bbb280cf035f2157220b29fa03b54bd5d1a241d735a2f4e06acfdd3f71b911740f82f9f3be10ba07bdef5d871ffec0230ef96228a50f7d1e166a3508e4799e9783d04c1c73c076becefb843c31d86cd1a3841b2f3818c5f0c506234a7628c2c6a81be349b9f85bf8ff058d7fe845b403b75ed42e4587c916a30c5ed7e1cfd6fd904e8328a8581e8f7089729cd2bdd875497861765c1c90762939793c1da4e65c49cd4962e7053051543d79228b37461e114e30f9a14e982aa2e618794b68ef5b30d091417b0330f2c6296f344bbb949c35188c58817bc5e83ba5ef05d3aae187382c447921cb9d2780c05161c41548204982a5a4e49411c110f5e4a4ba98a3072715876a8e549c373080e143c4020a0ab54bc581220e989c1421a4e23c49c5e1221507cb7ba393677d19bf8066accf053442e559102b03799e1525f85fe720119cee843e386cf4071e4ebb0b4e9bef809eb8cc5851da7f7e8846b4e7f2bc305d5cf5a067a276c344bb1b1ddc5461c5917a5346bb949b2f526ea4462858881b2c393a0c87ea60282d46d248fed13e0525c5482793daa7a0c8e8598b035062a080c8601d5184da527e5032d0f0c62a28293e060a6a5cd287b564cff7e3026b083ce1d412372cd61229bcf01d2058059e3002d0000cc133180459af21a0cc6a3d110b28a8d43eb5a4093d1b8aba57ace44ffbd492293dcb650790d0e49a2541b48072d0108fdc7a52fbd4122e5a86f6a92535e8d92b0ce76247f86288a7081ea830a413865c8c5e00f5f0281e2d2b825ae1876228d1442896744431ebf05b59eef8a0c3300ce964ed534b8a886544708f88c1f1c33154f2c5a7c48b506cb5fe4543fb94122bf48ce86363263b38d6c84140b9d5461445b11c620a3df3a8f18b1c6a9f520245cead6f699f52128436d3bd295dac918380722b03056940ee518205828ca40b1650ce4a5ca8e11b5b435d287e1806e1571886534a94e83074d1867418927a201831188725b6ca2432a87d2a491bcda3d301bace804ad0f88810bda0568b0707beae8747f168f900625204c3508b0e431d86651d563895048b1789411e05fe0d10784c2579d20f689f4272a70c63fc6a1c915f96415e8e03bf7bcbbb2022a4ffd0d0f84343438f5f415a74cd444c6369f08140a156556669cf73b54057febcbba1ecfede7bef05fabeaf736f8917c5c144294131d9f1cb1dc82bbb7851008a99b13407f78b292466583e848328af766f0a8995dbc170ffb29ce47befbdf7de7befbdf7deaba5e462831dde40e1828f9a303c1885be040d71e680a1414b14401885797468274a87a11d291dce300a4b3aa0f629244e3ceff415122fc642d763dddb798ee7b84b2c4521f03bfc75da854416f8422ca1fc5ddcf927a3fb30bc1e665d7625785b3a48639f3ab247e31f0f3eb8feba7ffde55ffacfa73f2196761708031b3703c404d7d1e386f6a4d4b819148070b0c3cd1c51a0038202ce01d40ad2a0a57dea8816bad33e75040b6d640aa0324e202155421591e487510a1db6b0523aa1013d479cc0708405ab2bb20378c839714a1eb2288f50810105998498f1ce11639a2f3a6cd1e186286228a160e1490f2c2021958c21c617e3167348315bc85045f944074822c68c689228a100415671c98365644385ccc105294f4a4a2a9e70641630463b5e48e1829462cd9333e3184d2801868b1025113e90567220ad9861f4424ad98411a830468ab809c385aaf18d1b12c9153929ca1aca94478421bb1041e236505269298ba8817c0206168a3419f7147922c3a7ace20e16384ab0217fa082fcf24479c3d4688511a4132fe5921d463c3690b92925935c0b53e48ef011a3f492678c2347035a9067d2904c9829c52c31e641e2050e20c103b9c395196448034588295c92f24a9f72093d40d021c768436aa166bc228ab20a13e41751608230d30379040719aa8c5451ca267d9650d127076545fa50876cd3c5182b4a3268ca2bc8903328c1c310250c5da2b28c6b68d8f2829a319890318c61a464614f19c60ed9c21b23b018bb98e2891325165264942246ab1f46353a94b1426e40036413319c8c5c88c142d9e58a6c020f4915a7481667a8d04051da20468a23c629be9453b6906e6e20a1c4304671f5a474e10ac99531c0f882cc228f73482679dcc20835650b674a2a64481ac2906114516ec0cb28678b972be41054de4429b5f081aac2c7c858051f120b3c75e48c67b658a2863ce28c1d32a31661a68a98e285ac620be9c49512072a5fa29035ec812aa3ec315282e12307cf98851cf26a8b3b6ab69c3143a6a4218c10459063bc90386c297db8e284cad8274a19061489270c2341f029a5f0c491f3b4054945cd1267b2908113a6fca108d20b2f639d2de5d315920e152ea2941ab082b2c1ca0829c587bc01cf18869c12882dc83e6a483e67622053e2c224298254e305cb9612c99592092ae35594226040953b80618444c3872c02cfa8474e99c31656a821d99c79814cc9254c0f458c587829956c29a3b8328a91452a649a2c462139a0874939a5878572cd18e41560905aaac63670462ab47828d468b28b1c6a97fa028dd6525f9471b1d8e07052ce191af23ca02ba0728e17893eae78415616b328e67b2ff87d9d57c6c93351eb081f7f50e5f7df9e2e04b37f5348a468ffbaae1bb5e7c275343879c6d2dd1e201d4408701d432c7df1a7007dbfec22cf88761005611bf7ed4cb1c382f69f0f5a6d86bcbf4fb423cf101a7fd07b91e7d280a84e9e116922ecda414e91bf2f70b2d7a58cca9345cf86c63999d42e95670622bc03e7f01adaa5f214d1a476293c567ad6f2a2ac1a42c5f0e0430b575de8d172e749111bb8483326870787e3947bc4c831c94180bb2cdc22ee0d60d2f8e05e7895db61e1ae0d1a80a385d5184d72657235704e723568c9893970456e14be84175e1ca47c8ba89c99fbd17901cc1850a4a802e7ce9d171e5843851eab2c90e0f0248b0db9040ce1c215b07891d30354146ea4a2b063270eb965882aee2001650489219ac871c95d80bb22f7458e8319b87c27a7444b4e2e0d063820275301ee5a01a24e0ea709234914397cec50855185c74b7e0147e7ca161cbcc8b3e54e79d4952b8367715f10f2393694d0c0dd29c6f81b2c2e1a2d720ac045e992bbf292fbb1200867e71ab9811b9bd2941983c3153e441c51074f170fd48ee0dac4e043981de4f81d31b917006e86db8554ce85a3c28ec5fdc2bb002386dcb56207cec3742077851439d80170787262721b10e1aac8c9c95d03e05a08a34a8e8b07703e05ee092ff0b4f040009cc891383c39173cce10a1143900e0768873f338188c0bad3ac2c3d8df3533b5d5cc217b5595fe46bb954cf69d523366d26cbf99ccb635652c8bca8ebee883117e8c50828e006cddad493b374ab3d96e6cec8f1e66beaadd7d02cf5f15778791e0f93380bb8fe0d95be34d7177d86f93768bd1ccd83bd1766baeb758cc7cb5fda51415a2d6f6ba9b7e08aab4ee33bd3debb499926eec2ba9cfdefed5f65b552dc189b6d7df11780318a0c8c711092318c000b076355bd66933b1b5dda2bf97cc556dd9a7d1cca7d1d010dc5df4eca5ee7ee4d9a3c003cf1d1a77a1a514c5d1aae99b7f6e25f4d3dbfaad9ab2ad3a022cdd42d025f559a7d4fca51455cf6f6faaac459b7e9f6aecdb6d82bbb73c776c0fcfdd05ee7e82679ca6c833aec1dd4bf08c458080e76be5b07337a9349af97b359f7d1a4dc368b773cb62ecabedb7b6da6e5bf357f599622a2de977cd64d1a69dfe96b54e349bcd7c275a1bdb2b7abe9359fbf5b6842aa56aac01ed36212501e9d902a4ea2fa5e8996edbee013b376bee3596a2cf9ab23335ffdc4ba9298ba5b76753d4aced75d7ccf463dbc6ae3736dd4fa3a14d4fa399b1675159fb37fecd3ff78ade82ecf553156d4da51fb0735f005d55597b333fdd3be95953f6ad296b7f9fe6ba64bb7d6afeaa2a3dd23ed518d2db705acd144dfad7ef25b386eed5f65bd63e10777779be46f875770a3cdf1bdc614829ba84ae2ad23bad5bc87ea7a43f7f277d6a3600c6b49ab5bd7e8a9ab2188e54fd5d53d9df4ca60832d60481a6aa494cb12d8b75706453dbcd9a32209b3565361f6dba856cb64d371a4395946e2cbbee1315a2ae36b3357b98ad79ee58aaa66d7a531ba0b44b60d75dbbb1a91a33571f3b96aa2314151dc9585366da766bb66c8f96dd4be8d9de6ced8d84a222196b8230c2ba936ab7b4031f1cc4f6096c0c6553746d876c10d67d9a5b765620030474b0e3c6416c9f5001a20c5e013b6e1318b2cff487bb07c0dd7f70f724cfee7eee74096559f35b357d596b026157d849436f493960186000cb0087aa94a23836cd8cb11728614044b4010666525206199c3a3050ca81434561ec6ecd35061bb2cf14bd20b6b21738ad2d8b2eedf50275693563b00b96d25beb74935dc0ae9b85c0051f142d4a8fc2e1b4779f9422650d576e17fdb5f2e846acee9f587fe48dba1f859ef02779a3ce7bfc2c219c87f37e743d2579a322de630fab30fe7d961091158c58e3df57a17c225c8ebabf2e22f7bb27523e7e098c7fbf7411c99d8d2cd98d58ef46ddcff2f8ff3fba888858425c42efafe7aa11be0a35429704bcbfef461e10968bc835eabcc746f8cb22dde3babf3eba0722c275a9c0fafb4ed8a5029723960b49856fc47536be27323eeeab425710efa399c40fdf8d58ae925cc1884879bdc7deab40bee722e2fd2844c44584f5a50a3f2a70095f05f231f9f88f4857e83afa71f1e86c7cac2cd9637dc912c291cff2018474016159c9a1f3c0acd0c514fefd9e475712b946d70adee07b96952cb2d1de771f53e85830408410df77c20fbabcee9dbc1ffa3c2c56951ea4739af85424871f7b451c28fd5e26f25d83413a3d7a1e0e452f24bd6e0c433274cd3a527431759ee7795d5762cff3baffdaf0bfbfe07f427e9f88fec4efaef898145d33f13b261753f8e0c7a3fbf05f26ef89648fe9be2751fabdb7fbee1b7f786b2b04bf2714a4a599c0774dba821009d2d2a0abedf49d797f5f740521a24317d3ec82a0678d95c837b07460fade7bec752b7437049c0427f91eff539223f888106b86fc94844b1006e8fb47bec77fe47371f1e151bac34029763a887039127f7c22449e92bc117814e2ef3e03f7bd273fd0452403e3831fba321092422c1a72064697e8ca40e8323203741999d1e58b2f0c254b4bc65f8a8e2577451f6b0b079d8efb9e5954c41d563b4451b4e2b3e24be39150e8195147945ff8644c0b684a220d04d9117545a06c11a178d1dd0f755ef75d953002ed53506080f224aa744fc1cc8241ec9274df3de9be9b82735c9c9529dd7d8705e7f8d1dd775aa2d0dd773c745f42edb9427d1f9bc154980d7ef383ee1ecfc13920a8ca730582fa0a95e1a3a44d12305ff018758fad743a7484b961082ca620c94109a32ea7bbc75db2e84e882d46601da1d4f7b179536545e50ae3f01366e269e9fe761122aa8516a4700ed69832683a5b101f1a532d30e9ca3be30feb8572cd38e5848b9f26503401a367af3137e564aa499a0ef43ed07bd17322d60b9fe745c5313305a7e7cb0f675690c244ea0a2d7468e2e68a11c67da77daac9115ace569fa02456c852858a0c513838c112c5a7a08839020f0b3120a91ac3025de783d5244a932e3f4daefc48a07daa491339665a4d8e84631886f123c2c9e1152548057e4ccab03c2651acae63f285c5440b93299989133a3c7a30c1c9f809c32fca2a2de9d76dfb1d4bd5fde7665285a4e8efa45d435bb53581bdb566cba6d0b4d314f65e956aeaaa74d2d4b7a1b637083cff9373db60acd9f44060ff0eb3800a6663555121a66c27b168cd022160e76e329b4cb000c93d699f2178b64044cfbdc6fe0612d23b257dd35100dab8bb07804a0040b039b5676bc6d033456f4babf93433453d80a56a6cb36adbb22baaf481e70aac2a3093de7678aee009ccc6a9b6afcfe1b90220ee301b2c6c09b65f7fee0a2c0832e3ee30965569dfca96d0b5bda99f2ea5e88d1e3eda5b5213abca82acb4295a538380b4e8eb6bea6a636f6cd04b5537ab6b54a5e89f7b5559b3a9a69a5043087798ae817ba41f34b3558f907ecb8e6aa0a0aefb02664bdb35f2eda4608c3b0ca98393a682505474c48112096d6aa2ab79c249536508f04c41126785f0f04c010f21681b33f7f94ea7b9575a8bfe8ea5287baa35354dcdd8961d5120bac3904a3877cd4c09e079022a77189207434c1190fedcecbec0deabd2081368912da1b257d596dd27b8bb003c4f3004c75e6d4eab89c650a59d6ea33c011077d8abafaaedbb7b069e27c8ee34b3c95c4f165d5b76afefd4a22d36eeee4ddfa6bbe664da2ee8d1a6bb96aa68071cc4f609aaac69df68a69096dd6707471c289160bb31a9aa038a8a8ef65e955268da69cb0120a6e519003d000005d8a9cada5d1300add6ca60eda6d14c20b036765b5bf68bdad46cfa9beaf4bb86b60948009379a30213307fd74c1f9e25a842821e24984950c1069e2378e3b05565d1750b41776caf7fa6487baf1e20b1486a6abeda16c073041e54b9bb099e816600fac0dd5f9e812c20f20c419dec19022cdcfd019e217002818e07b0784089f6fc0008ee0eb3b16f6bfb7adbde4cbfdbd45ccddbd2528aca5a1b2ff666c6fe066bca4eda4d95b15bd6a6686ddded3ecd4d53d5a4d75f8403d6a61bb5a1b1bdb6ec3e911e070c068bb5fb6fb037d8753ba5a6539269dbad6c09adfd4d75ba41a3a14c6faa47bfeea41b3690f66d6d915ecfaeeadff81be7668aa9b4774a4d3635770dc6a6b06949660a0f5b372dc9fcbda26dba8f1ee80ee0990695c3905a555d919e4565474aea6ad26e4b7bade19906961b9e690c71d811092494b063abaaf44e49bf6ef6f6660a01771f3dd3f041038479ce54ee3e5e003024b3a5ed13e9b710f4cd679dcc269ae994444b5527b3e997d2d664dffc143db5bbd3f09cbf640ab83bccc9acc5d69bca7eabaa6bed5bdb8e0dd97f0349d69afb4ca169dd4a48cf9a437cb83b089e675431e3c88c1eeefec3b30c3e328c7087a5706ea7a41bcba24f335bb5fd1dbb9d7b7da474bd2521ddd8b424d44cd1cd64d676ed653154d6f4375876afe7697e0ae97a4b4ae1d933bd29b1bf99505a0a819d7463b33453e959f491b6109466a660db9b09295d6f492dedc6eef5cd67cddfe7d34cb44d77cba24376934adb496fbb3d7ba6fbd90224fddeabd29fe82d084afb73a34a68bade8c646c1911fc44fd54f959e127e6301be97a93c55e497da716a5a14928cbaab6269546fb14fd96bdcd7e0b3165dfaa69bb69bf23f0fa73eff4d9011061f0ed2da965cd4dfbd67c25f4f7567a21a8d95484a4ca9a901e070cc904a4df49dfb23711be0807ecdc1028814d55d6dcebba5125b44df7f98329a63a60ef55e9dc42d6cdee955d32691e986b3bc407bb64d294d27d017555a299dba63aad6a6b9ea9593b3a7f9ce88ea526cd346ad39b5a3355a5f487aa94a2abbaae3e7aaceabaee1464ac292b2a3a5af7996e9665339b340b88cddc6bca22f220cf2c31dd369b29fb9404771ff25c5239d168372596ddb1d88dfd73b79bf6a92ebbec35f6acd9d4b4d3520aecdcad79da6ecf9a4db122a492872cd69ab5ad9e4aea003c97d9cfddb6682de9268167b2ca1d76aa23904ccebdb2e752aa3e8bfe125ac381b6b45bedb5bb03792665ee0ec433d94af7bade62bba9dd49bfcf57db67d118aa841a791e8b5080e711e7ee30a5473aa1042435b6cf5fd54742faa5145db7ac457dc0da98b9b643f6abed9f9b869a1eb8bb089ec7d7d27ab3fd528aeea4dfb174af5b69afafaefb02385216fdbd6ae0597c736e59ac356d2c7afe4e7ab55d6dbfee96862a6dd991bb33c0b348c56ccdf3778bb2aaada6c6bea5dd4ca57de248d5d4dcb27da231f466420608b839ed103a70da4c1ae4b875b06f34f304a71d0207b17dc23ed598bbdbf02cbe7f8bd6b657e039a4aaa9ab52d8c5614a6a079e43993bcc666ed9b30568d35d4bd5151592ee77a225d536cbeaa41d01762f99360b3c8721079e4132e0cddd23e0190c00e8196cb93b0a9ebf2877187b043bcdddb229fa482920fd6621f0b0cdde9e66d656353df799ee5a4d8ded359216a8c42a98b06a18448a2912810c0000003316000020140c8905c3d12047c258f21d1400086dc03c5838118a678220c7300cc4300c022008800000000010820008c418431adf00f1132f97821cddb1b6c9d055e3138c4f244b021a92b74fdc7d247fc7045c98c1928062ba87e31a60e135ac952acc9e6947451f5a3769ddeaa685a7c1a437336c708281ce24037c91651f6133d237bc470c85b7a8c2743ac50682394eb77fef803bfbebe8c95425fcb690d83a4e70d6b28cb7a1f54b41987768f827ae1b1427825fc9ed0d557ba1eaabbb1cc4f915ce5f86769df48e81db81de511f6f4d5788adf53cddadaba6cadfca5c4d7589417c640f96e725db33506ff4851a66d82a15a82f1a9803cf0f64a7961f7c43c9b1664bb932f32aef3c448ba3274fc92a49186a35b07b3b10e3af27ed3a3420afaef47a88edee11da2c79bcefa13ae8506d7f4ccfad36e8d897b675ec5bb38996b6546965b579b613c2868c67b2dce4816a599bf8787ff760c82498080af2c359906a7ade5b2937230e08a477b8e7d87cd633db85419b9b1760d35808502e00391d8c938ecb7eaca83e95fe74381eef2029dec3c51c2661831c431d4808a742da6cf01642b2f3917bda41ec5d05d3bb2a6fccd878766de3791f02c7749b8f6d9d52dea13a5dc7b38439a25ba52c6eee2165e5fdff1a35d7607f9ff804caed527000f62291c03a92f925ad46d3600ed87256b45a763887a36b52627edd6c49e7943eec70c11b1c89cda9739229f12de4e87bac0242ead50e21d1b1e4429741f06cb77fee7429ac72c98a3fe6ec9de819b466725b94d1751f208a25007fcea655379e83a9d62609ac0aede6036afb723a8de72e02773e4b5ffe1347dfd945808a0394e0cd0f95671b7e3846471756459d1d452df0d247d9343e35a2ea936022966d92f1d605c22de913e881e0ce0e56ed765ad9ad873ae7524331dcfc1d5dac7df7e4dab413ee608aab3fd170a4d9363e0f672f90bb1d825366a38b5612054d3e024e975bbf17c72eef901523f92af72fbc121fcd5431a6a14b787158770972ca510cd379c76b231bca9420275ce4331a2d3e1c0cee4f829124fc144d198250a4c9cbf82a93113fae307889851b35f34d2a348800c7cd32a2d48b03db7ad82a7cbf50d35038311144f1467fc4b1f76a0e350a47ad8ffac4e6a1a2048f45989e9ef633c3bbcbb1f417ee5baa1705defd4c3329992323247dda967e4c692921fcfe59bc418d847f391114a1e15bbdbba817fb22b029a86f67100e48677229661d800c719f388c1d121b28d02f519f543e139997fa1a965227d5768d5ed72ee3b5d0bbe85fcab65def5575141ebfc6c0f218f3451703e59d8593008b7cd02dac310f123a8258cbff90ca1a73a07013caf295615be5e953e4ec3f8a245387f755cbb90480d59103da90460e71443bd7a603b3ee4b67d42089f58ca504371a60a366e32ab08d56d92547884eb88dc0224d7f4d9f932f3db07a6af9781abd99e2453e276985a3cbe1b758ca628237b8b296277ef926c09d99a4e225d757a690a045d2f2f72407cb5a9a73e4fbdaf0065c98f0195bdf1434726c3433ff9b8abe894d6b6309c0df156dea0115aefc5dc71742cc9eac9921a8df6358935f627b29579f8c79eacaf25e87d88998bbc3eaa3ce44f157427980a4473f72089d3f203f977d56a1a9aaef691a9bec85a697575b3abaa762cdace3bfbaed834a49f2ba143d418a0497394040545c95683de5bf3f2749af88b0f79d80edc6406bb3fc69704a1df6f6cf42aca1b2ade49cc7108e699c620b200e6cc45f2512b96f8151babde2c4d42356c5dad184f657c701c2b0adb64a45f73272f7a4ff3fd1c702850bd6b0f9d11d5b90ab19012f6a69e7c3c897e810cc59f0b14f488200af0cf75f33df642023946ceb189708d59d6a7837b7c279e6dd5d9ae68089f9cacddb4092d6a9bdac628973cde29ffd8f09f7e3726d4a7efe75318d0573e2497edb1fe605939f02b94755fc3dbc75870c3f683eb1a757c9f6dc563f14af7e2b38c0cab918805bdfa6780d9b96154e889eb7913378408dfa35d14b993f5fad0cf47a51a5661180b83bb2fdd246d4f4ed9abcdae999e64230b7e11672f6820325ff2ee763b6b91b0e0f19cea1e69c8f5a3c1524c99c080ae1b76d7c419417917330c1958252f172c20fbb384501a12c3f658ed0d0ecbde56143ef5a9ac507d7540bf7469a2f91d63a867e6a9673b58c404a6ea6586ddeb0c21ec11b17b8bd34283259402a1a82289f23850a0f2d20e9978c81f732bd651ad3c3d7f9655ad66dbed573e91ad4d3f3a237d9929910fc3eb601b6a511c3f59c1c005b06be4b2c2aaad55023605f638fbcfc9553aea87e2db532d93dd5c3cf028bb9a72cc2883f3aa240f72eedcbd3bc686b34caa9eea78f058b2d23bd35839e57a927ed6f21e405607ff20247c9d1e9dbd573bd256b7b650fdeecbfb94ffab0ceb67849d89030d7af32e33ebbe9f45c55aa814ec4be5fe13df74d957ef810c69afa4b463d3a09be97557a9178df4ecf958fe1b2e0a2bc4d0d989c4b8f5e2c4f5e36be426ecfcd1e9c74f1df8148f816ffcec8014a52f4575bf4baf093ba8b952ea9b1a602ad9dbb1d0c7db8d0911e2144b06f67e453837739f4e03dd93a46cb78b1075c882a0a664f5666cc6fc31a6dab8d454fbe6190d2a14c83fc3feb34551b3b22ac416edd88ce19511c5cd0c397ebcf0def5ad643cf25fb760cf9ed64c1d4ceed0a41412d9efe71459a5ae8dbd33f77d4a5d7dbae59865773ec9a607f4fdf57a5b1b282b0041e4b59b27dbf101d3e5830fe414b968bfcdaf95da9f51aefcbdb5ef7e829f1845c89f20ea53522338da95d9c2416243facc505ba7ac050eee4a82e872a4a1afd4acb733c08f05defa2e32ff1b8a50a7d96721ada3c42cdf3702f65339a633dbba3ea894803c35c9714a2233b227d763176154056905a348d80a5e78690ac443585566c0b85c7f49d5b4bad1671821a084701385e95eb88269718fd786a125542a3fbc09562d48650441538bcc412b069c0dd1109f97686aee57deb6c4f8c88c1043407f4415364f6169a7c18edf7b0f333baf1cd669b63b94c8c0bfdeeb3b09c4bc61a384f1f97ca9cddbe73712066f5640e905574c6446eabeb771d158796910514011e341025d1b0d1185a9423b4b72fdd2994afe0c8cab57a20ebeaa034c92a43bd05da170ced98c947510dd37e772a15688df239c446bca73408dba41c979112751ba320663d127230e0dc110bcf92048545169da95a01abe638f4bcaedc6335861c192502e84cf7d352ef152c16dd095200a1d2bf88eba53267eae60e5e9616f0a1305384b81b39a5392dec27521e3dc57a4fbf944634aa793010dccc3919e957b9c36f4fe65f5d802e369982d0d89a4e0438b24aa72d9c5cd102388fd91bc2e0851c672b6837de127812bc5b95a2009117ad9941f3110208c5b9e4bf441ad075a30152551a37411b7a538dddb5569d803e573cff46ecc7783d2eed83abbf4ffae89afd46267875af6c266eb3eea83dd7fe4c45cf65293f7361d86356edca982995e206c37bbb20e215ab4480d7ff7840525ccf684410a4ac8cc888576648e1f786bb5ef97d58bf2a96535c554f6d8b290cc748b998252aa0c29554eabfa88712a5a705f40f8af03ec542873f2c16d9bedb1abfff11e943f5613a2c2a04b1a710f73907c2ec9f66ce1c3b8b4df0a6f77cf33ffe40e8cb05be7126c4be441bccf7a37ec74e2552129066010a5899eb635d07487ead1d04241468533a1fc375a3b24c3c8329648bb69d459f573c4ed26abb90f7923331a26bfd785658a4093b13b6968a19f30d1e78d2b62687ed275d8bafb2cecae9115d3e190bdf1dc2c4c7462fe890a153c2678d0a998a8f4f039137ca1ba178d2370733b94fa5658cf541c83b86fadabe39867c2557090ebd671944e6f61456de21aa28cda09c9472a8f2612199186a11f88ae281696260e178aa7a270f5638de2b72d65b2a8418a1a17751204e2a0cd94a5837fbae000cc35ffd96dcc07c9ac581524adbd35e79d3f6875d05a15a74b3b11c0002f82e7a3c0ab84d3b6000406cf747eb06ead1f4db23c5e2b7982074fcb726f5ac417e680d420548779d94cf83a78e5bf154d3663b94231c3c55435306904c661394441b1842de53649026e12cbda1953df1ba5b42ec9c730d3235988150061f247461e63ffaed0f0224ea9b2b2af04aa07556010b693560bf642224e592c9f6abfaeecde8e5b678e66c20609445ed795664f3aa6b16ce6c9345ba83903ae1004e78dfe87ab9b54704765cfcdbc8f1d2d0257310f4dcf8e939b580300ad676145e0fb1e1d5201643d3815469eb55076b993b918c98a5e78007fabc61cbc9c1da7968247e99e5789cfa4f226163a3417083f57fae72099c01b234214ee2adfe8a2703f968961258b29eec356fec3ce7b65b5127c9a19d1b777fe55c2cab13588f0c10f749119c34e64afd3f86ad8592c9cb24260d24d8d6d5af6af0c3251cb1db02131db782fdeabc0c9758911e140538c6697eacf66d3ca0cb9862fc053deab492b7aa6149499d565cc94780bda05d85a5cefc5b480464cdd92800f2a3c9582658d58183644453b0ca3ceff4ec33af7439bd5c8e6e019f49dc92608b4322e46281342ed6c6352cfc668401cedc94cd186b6b1cec1701fa7adcd954c6449dfe5ab730630ed6a2fcf76cfc6b478114c078af7a485739c1adf3c236f964e936d02b9a8c8161b6cbdc38c5a71e9f3e527a0a5d92e095d7994e8985590929096f310fe85049d577884e7023196d8c394ea159a329a7caa81459ee4697ffcb28a7211c5812f1582d3ffc254627d050563b891a701ae721a50a48a49e6fa318078fdec2bda2b68e8e291cfa3374e7908145d44b32121bb48791164b46d8307b4ab425470dda8c73051ccc999c1cb3ac269d71437df9ce338ef529ab5916624c83aa965ef0806b0b45a164a6717118ff14838c28cfe0f0cb710612f8f091c1035bfedb34de174038c2674a16a80b94719e9287c2b7be0b7ac73e8a183372e564bc6f3ec11e85b8abb525342f29ccee26b86c633427ebad6cd5abbb8f875bef419f70ca4fb148933f368eaf3b8ddf25dbbd04ec6362c709d74841fec93ec01696dfce8e9e33bb5faadce93dbad249d0cc2cb12328bac946bb08947d57938af68e987d64905034f78b85a42b256ed46f07c5dd3596b5c8bbb8299cf6872d524b2658908f24e1610957c6c4c67911153de9655ee4ae8c1a6a2b0bcbe230518fc838ae400fcd538579dd12a2148980a590cc98da56a62d1751220f71cbd318e02d7cad667add9a2684a05f2b993cdbeb4a2d5d8dc12dda6e48dd0a8780b129822128518b5035394730ba49a8a2be53bb735ac86a528b9b1775fda0a37d90a6900a691344ecb4773f09623986808b8fbcb6ecce6531bb64771f5bf8d4bc661dfb128a89e226b8e01994e9c7c16caa11d64efaf7874c949bd5fddbd4a6a7d6d8c1b6beebff83207dbf85b9c8793381fd94809c1d149a230e4b25ad054c0970b76aa3f54df806090eb21ff77762ccfd02637d800cec97a2d33a057a1b0af5fb70377ab0c6bfae55f75d20b979d35663238666ac174c81f87641e33e9b3edd75818af069c3418ba4a17d18f5d1bdac961f109f892f6ce62f21255a424c01e804258823e43d87cb7247daa1d0c2159ca9e2c3288811a90c2951530f44dadc33add189e2706d199b9253ca101e50f367cd74eec279c7a8d05bf1714f3390afbbc56f13a8568d3337f914cee5a6a6d6af4929c68cec4a30ab756ad639c942b43d6738a20ca67ed20cd931e6160cb45b0649880b5c259b3ab2a6581dabb2472c02b0b36c329aba3434789df273d74820f3dd7cbf4f589c240e6c135c6ec41ec2dd3becc2dbe21150b7f121987d3e13872209b192dda06943cf4b450ea0900e377b49c2d9109b130e12311149b273b4dae8a084274da0051fdc2cf04339e54becbf314f23cadf509867d3896990571777f405aa787e498b5c9c2484e844f71c31250fa138cf988c3c878db2b3bb5a4c1e475027f70d8581ab374f342064f32409c1e9c23d76f9e22606ed1de7a0cbddc8d75068b519568f58b2f63a515ed80ae14146bda2fbcacf54ee97077d1468b972bf5fae2525ad8fe00e00645ee1f65daba346804d2e8bdd835c56aa8b8e6f0b1bd5df86d13ce2c2807deb082aab166b4888a301152668426fc20cac8831d258906d5dceb45738c117c1c46782b88eb616d6a9de67828b32a3e5ea8e2f468de51aac6469967e3dad6d092d18e1590588c09ed1990079f1f4d38b0109bf373f152be0da8722af3a1ea4f5632864a02be300c4da5fb8e30a39005e0671be5256e76b6bc9e5779605f7c1e7732f084106a3a0ce51c92bbf2a702d8a6cbe77e926a56f566acad276b174d5db1332e29e1f010149ef571ea00503dffe0071bc5c2f9740a0f1d8a9cf03a46588a5eb569aa898eacb61a77c5dd29a5800e0554ce4a51980ce0758d33380bbf5adb1db1fc012c9cce3b8680b9852ba3efed17650ad913121bc915995f07226e4bcf6175280a18620de7f2280d7eae417a7cec0477da4a25e4b48de8a14e6329d7eb708174d408186d9c0e46651e77473924040fdf5d34a717f713e8a1bd7ed942a365e6f27cb28515be95ab556351c79144170cb0061a7b45de846f238ea68ce17f8c2169f9f8039c32037b65fcffabd358692c8bc789728d0b2a0d88fb749dcec9416d1eb11eca8d10ced0e50368b86d369f240f2ec9675a3958706511ca93c40345a776f01bfb1bddadbb4386884edd9aeace7ee0aeeffd9c360740da19a782bd8486173b6a5c22937b4024053b35598daa0a4c507e497fa4992f74ec270298b3eba1649de9342330f84760bcc6560b194d022e78d96ba7824b1b519e4dc0a5119d89648bdfbc3754767aaea88d214e158ac652da2ddc40466d4c1c35db877123d116bb63017bab0eb2cbd71f65cde79affd5295af2bd433b66d668a1c8eee26c82bb29a080bce12189bc145753c9833f76c505ff5cf8a3830a63edda9a01cfa2584f510dec380b5183053dacfcdbeacc3912bfa5c9558d63c219d794f0a3ba7ee8f9943bbcf545c21dd172a1047c1be9eb1cd67b043842a3938d4b75daf2d8fda2c1d4a7747ea6b9c3d9a339b2c5813206b5d12a9a0662c1b5934f3626a8e79246969622d99d3707caa9f35d0782b5273fb7d0e15aa807535b71eba2657e41c8416237ca9235b11912b4a3b5a8b5a1b281c617b48801edbcf9ff5f1682f5af2db4552a743abae48f392e0c3aae492533b1bddadae590d665750fb98726ba0235dbce4e7b532ea39b005e770b1120ae48c1ccf5f6aee4f5bd78af8d90ce352ad0c59be4d0a3b072f178d5c849b03aee92cfd51966c6dd75b61d745941765565edd17b3457b73bd0bb40ed2d1688d6acd68b27c70925de7685a918c54c256d4f1c8f70bb0b79273e9e8e5227e364177b3562163773ebd75fcadc97e8aa034bc68c4e1ec3a064013cd94eccf60cb9e71c42cd9162f212fdf6e2c4032e576b41a1af33c9f310965707947c8ce48444036111e638be700137776b1a5f22ece8b8db7d1c477c9c02d582095132f310925bd5040ca734206a78597543f58d2a4ccd2109d1e50e3d639d2f1274a24c407b4818bc8d163956ebe0fa2142ef9cfae6b344092ce31ccdf7626ea6684b83bdbe666ff7742d239f0dc3924988d7251d191c4fffe923736ea8d0f234b72a4ce2b8ccfc1dd87ba9b2f570671a4faeb167838351e5938739f75cba169ea99f8f0ac9f7e8f0cea5729f2e42cb9fe8ccef9b721b387db9dc51a9fd2135a80c82d78f185b5eaa0c2046cae8b80077748a0ef299c082d5ae9891724cc86bece795e3f8721cbbe2a16741ce1117efcb787c8cdf872586d44b7552defd1f92ccf25f5594ebbb7f4fef58498590f35f25b64205259c3c6175b750d1bdd7e2ac911b6700f14e3b371f86ad5adf45ff9f0466d2af820c93d0ddd5f0a790242b5b37a54d6e91d97af01321781ce70eedd1ba24f850f095a192f769c247d59521dbfd550ae9ed6eb6d047a4da8c53268773534d6cd10efe92de7c5b40b717940b5a6cc5f2122f5193a4ae9593fb91b2b601e02e3c06500c4f37be224a744bbcd7e909a9f723ac24d2e7e425089a9ad0de36c7c713eaee2114d7358c852d016494770efd934ae0cc0f627a0f65a87342965512008b1fbb886a4c9fa106c3694927611c676fc59d77bb1c15403588d28e805848ca906ef06868a3eff1aed68b84eba54f7fff9160c95617418d5f8f50e0332fe2129a3a7c0b9b25210515a74e16e92071a31fcdcb9f48d0218b0644d641f8a971e6e0a6325f002cc5440e336e4b1525c85f447faeb61aaf067ac7fffdfd9dfeabfe08fda7481c55701a364582dd467ed71a3fe924ca6d1fffc7ff3b7fca3fc1bc04ab63304ee925055c05a32da12b896d0e6fbdfc054c9524ef21790255326e097f811dc34f003c4bb4ed4b8dad5a1acbc16ba7b84127e2ef0bfa0bf9f7f027ec9cd3a3f269ba9159418912f9cc622413fc8e6d0a9f458bb980a0a55062ca5a9ba8cdbaaf24904080bc429976ca143c947a3f85eda54155ca5453183abb4e8e631bb84aeb5f90fa4259605e2bf50b92435d19fd2bf84a1d2cbd4e557f894301697bf193f824e8945a96e4ad06fd4ff15b2a57dd501e3420543e4198b2ab3485453ffe9cfd44fe5174de015c41fe9eff5fbff3efd0834253565fdeff3efff79fe634c066eb790da254da902bb846e6aff1324255606f2ff50a1645a821fe6fe0250c9dbe43635e45cf3ee3ff89618a5c36ceb95ccd83a3654a460a9d1fe1be84aac0a9dbf802b61335d7f732603b51b427bc9bd2ac82d79d3de5673b85057498ec8ba45f85eb8510e4bedf975fced3f2b7f8355096fa9e14770969c29d1ff098c926ab3fc6f602e092ac3fe02d0923d36a1bff45e53f89778163c7f01af927966d02af96b18c02afdca5afe02bfe46eba7f992603ea1b4879254c63e747805de257cbad27ad3209340b4ad5660e99ea71edd548e081b0b82884498ac61f81a9e4a62a5897666e09a89253a3fc4cbc602a5f72c53ff0aea7976ff34365b639602f19bf0a7a2586e9ffd9fd9d29cbc2e647c02f695fd6fe06f8255665fb77fbd9fe3742595a9425fc056809656aff6b0029f1751594a56523fd8b7fcaff066169a1b6115fc96bebfd3bf029fd4ddbfffffffc4bb829a9952bfe02ae244da3505632a52a484a4873ee37fecffd49ff1f18946016fa47ff9ffcbefcef3fe31fca1a7842255481a644e166e0940431097d4bdef2f9dffb6fc02fcd7b157025692a045d894929ff37d82cf529fe8fccbf035dc9db55b029f14dbf9fd9dffd67feca7fd91fd76febbffd93fbc17f647fffefee6ff657f62bffd93fadbfc1a5b4045b382e991be8d7e88fc0b7246f156095cc6f06ed927f7b88b5f42bb63f7c7e854f89b56cf9bbff863f8a69a1a292fe8df03390945c97094007516ca90eb7680230e27305f60fff9fbff6fe4df6dd345603ba151a80adcf0b924290aaa2638c6c5d6f1268cfaadeaf5f9a3aa6495b0eac3a7aae35269a6115e3c97d7ea2bb4eecafbf3c3119249bcf933ec791c7394a741bb5027f2a54ce3c7c2887975f445f0d1c0afa1a344f6a49e0504fee385d2a7dfb36c18f13b146f047ee0af794539c9c4cdbc2be48053ea76312219f9ddf2e4a7b75820035d849e0c6929bd8a8e49d964eac4e36b9d8ef79bdb7f20cb97d88bc3431e19fd2bf03e1523eaac05d72cdfbbfceffe25649cfccfb1168977814f74fc6ff81aed4323bfece7fa33faa29225eaaa8b988925b5527a054b548ff597ff87f509341fca6cfc7ce1895cf3701cfbc3df33c0ac848787a6830691c30abe7f3bca30cd3dae19658f0d0fc86d254308b8eed018c482f0252aa4612cf8a85f5f4983eaddb879ed35bb4c4b6be1d6605551d7a04658ce16446b04e6cd326da5ad06b7a9705e90f48740fec727c93318d9bf326936c36d228236c946ab4bc2c474d77fed3a500a474a2d64e9a547f3b9341af74fe6fdaaf28e706737ecc6c9a81a98047be46dddf566e8c71b0dbc50362a578aa55f34faa79040fc609033fc73c607ac7149a4acd24b228e7a71adb10cc5b92235248e80b57231d37be270cc5eb44ca2d0aa5ea8c021bf02d2089808eefcf8287bdb3230e43f7f072992c1ba4cd4fa6efe668dbcd0d97723907343028bf65bd8373875f156fc6a780586b3a85d2dccd303c44bcc6bccc11c1a5243c5bdeb34cc353cf7af36461f226ff21309b2109071b327112e44974772e937bd3d0ed61e3888cbfde93619bef661d1dc75bd32b51bed73e7d67c307661c58ab827199806a1525e3c96e270841237be7d506dec990079c2d020f97169483cb636d284d7517761b769aa38f408b0cb7a64053416f45a22eea1e0c177a81380d3b5e14367339bc9fec281e9a8492df61d6729d95201df768c1d6729db593d6274186a67492295a03ce100999fcd200f1c50def717ca56176b9b9c7f8a57a465e39e6f99d36c03c18184c323f4592f4e9e0be535b3ec2b48552c63a7ee9356a5fd918e18c6bdd334ff9a85ca9cddf3d94c0ad8a7793032fe0c5dc8d1a45ab351eca3ad906b8d134dbf177bbccb20d3567ae93245cc49bea8007173574f2d9260de9be91245044fa4d66c6e9c391f086c5432d84df6387f7ee9389b78187ad545da8fa0231491f4592294cbb1c561a624ca266fe16dec98b8bbd685f5568eabbcd74e858118ac7910da203bdf7406f18f37723f8fd748ef6b3634a77800653182e7ef9a0ff6dddde3a10d1753544852cbd528839cbf38ab8779e34d6d305d4ff9a66ae7ee3c419126ba9837a8075f2fed3dc84d6af27692dc25acb26652413bc0608c0e046fd6a56dc24690bc5f98318cf8c1b2bf0b1f978732649146ec87d3142c82af7618a6c5211734f58b36f54da81732060bef3201d9e58ca941e13553a76874c2f801ed2df2b68ecd0548b97fdcf556ef17cafd1fed4d901a8dec91aed130c164adacbc37994bfa74491012292f4abc43cf9acf48d12e1260d1918821026b20f3a705b529f2f166e328887a1ec306d7a273a40dcd7cc4921f7de71a8cf9eddfd0986f42dabdcc81a2fc16613b5da8f955d67d3294f876beea4da85c8b88e78087c379da38f66bfa1059748452a2b8c21354fd09b7e10c133e41d14157a4441b39aec48f255189afa25e860f246d80b52a8f9df5474088918c3945e32b3daac187b7cf5e68603b1daa6e921ad6e7587c523aa59c62e51fe3c4ed1e557a5ae658e0f8e5bf764a71e2c1094704e490c7fed309fe3426acfa1f399464def788b5f6ad70276b7fa5a6df97ed4d64fbc29d02fdb3394fa9b10f4df1596c3763077b17ce617308f4914f9406d722e25fdbe5b32d26150dfe31105dda3f607bb2135997ace2028182e07d668abe402134050b87ef5dd5e824c3252eb87bb8292c62983987ecace121a314ae07fa6572c5841cf35ebed6327ed8c9e0e31110e376a2f69f6a9e76a0358cf2643245dfdf0f212a270ef27722275f49019e6cc1658fd11382934cfb2b94852e999e0ccfdbe63eb396d8cf570bd87e8aaa5bef798678a475aeb0e09addbd0c7386c9f097919a4cfb1e714a28726655c8196257b02c693a92d6f975754c2db7318c67527321008a165bb00d1aa46c71685ce963b0b4d013b1c897c11b80f3ca428b69afe4916226c76135213e26f1e3a4f5c147ceffc12aa450c50dd42c325d4ad0b0d55c69bf90d72e4745e6323a10247e7fbaf58d2d3fc2c53cbe4057108f45941f092d379ddf2e31b88146a10f905ddbc39b0f051af8894f3d603e4d69b6457d641e66f53ab781039bff5d7fcb41127b9b8f62824860fc073a678493e0cb8adc73d3835c4909d92f28c7ccf5c1c8672b3c1e339804d2e50d21011868637903f1430d293886c80c50d4befa906315bf0335f79214f7e7d46fd0d3579d1341975c4118b328f573fd140040d3fd5c6286c6303d44090ecd069a10acb7c6c33327a1f9ab18e22e02531d9276e6980c758d74bcd2c4bcb4fa5c86b1219ecde21bd3de2250f857dd54316e132bbb5da4903a3ddd196d7770901122e03f89f777352a70034a5a3aa8ae3254e545cca423d31d63c79a064e974a480008fcdd1cc8ff934ef1c7af066a5cf35b1feb908364696aaf373538d93c0ac3be3b9387e7c5d7c04ee838d42b5f546aedeea14e983c89fb9bf66eab48d2e7ef1d59e9a131791ecd9eba7ecadb5549e59282651493e947d0f511831d4d0da29f1acc1d120c15580ae301c48f8b619493028961d2276a4d471c08d4ea44a9f140369bc5b3a5f15a233aecd7b69ad3d11eb48350d00dbc5f2ae77244d389b19936faa5b81226b044808b5d2a26f7cdf16aa0bcda3aca6602b130578f47ca7217b430d147fa90142b96820108803f2355eee20e3ec6e1ad3b98093ca700e1d25593985950ec1e9988cab174c5c4d1d8fc16482228741c7d18b7b23976a2dfa3f1044399c811433ee0e615e914a6bfa457469db937f46eb441b75daf5fb2023f9000741d472ee520b1867c6124c0d396f09e714bf6ac0b2e5f5e0869e084fddcb8e9287b1a10456243b1ad8cd0ba1672e6d6528f06c55cfb70cd4d33755782f098f7079042e8192e806aebb93c9b85813971252cf71bea92360e7305d2af144664ffec334f214caa91b6022cbe1b568e0f3402eb911a8ce19d9b947aa5c6c2aceb6387d94f8bd6fd126abe196d582ea764b2c17132097ba4da3981f4df7e5ca967fb8888201e8e535fd5b4719fd575326c6c187f7ae164169ce8164a2cfc9569138328d45e531790df645253e4371a6c95b683717c1e910d55b2b008a5b3c6a53734328d0225d5d77757fc6b8eab27f5470f659c692f975e34f1da73f2e9ee70a057ac4c582306eee73320d6db9841c7528da4fccb2bb081507f5ffcb774eb6bb52b87a65f673bf12427e289b3e1d29ad7e0d0215f655b358cee02facd3f351d8a70192d9eaf5a129a1ee9d42875792d168fd8b84755bade6e855654086d892e350cdebe0a9009ae57b23a705613bcbaeaf413604b2c114580c9be83633c4679f01ca2e26185082063c1146934ec5edce40fb86e1be2c92e64c38049ee0a1f47d2ace0be01e095d21f6021a9415112d0e24ee102998d6191d0531de26bdfd6dd802484ec0ae527148a3aea7382c45fca03d620403c268d822185c9b222048287863dfcdf86ccc14a28a098bc263222560a0f847cbf4d2fe8da56d80c629d964f1a476661aa57dfb58bf181edb639d364fa8bb587ce07c98a68128a867e621a36ef0861ede223b5973143d020e0f8b220e63727f38bc03c2903ef264b1528f0561f561ba61da91dc0b38ec0c4bc29f6b010a21e0c437cb42962eaa5a1a03b9327872f431e9ee1837fa108633a1d8efdf0c4862f09ce9466b398b83949228211e3a14d998624998988b71e2bb8a483f285686f87bfd1d850a348016ec7ad884ae9799105d4c1b941b71beff34f681276148a51940b398ab15ee24e751b457f9acf57b8daac520d8b063cbe75613519923be516a85591bd2384f6d7aa220ccc0f64a7dcc43f490d83d2d73f34f47c6dce57427e0dc9054045e368ae0b1e17f6b783095488a7956782bf53c93853f51f2282968f30045c1fa378888e2c661b24ae76887dea357a7993a7b7e633b0484ec516aeb2a57735ed63223e195c0126e88a492163ab1ec93f33a64e87d557bb8abe8a134a4d72cf9d1b2f743d4c665e104aa9f96de8347fde21b22d568da941c1137915781b5e857c13a2928c2513ef00687b7dc07648f56f4710ca22f85337c3125345aaa816b96825d12c3aec76e3b5086cd8b7425ebcedbbefa0e992d33d72402e6cb7e521f22ea544db40113f037fd74d21d9b8d0b33abd25447f2566e209e514804bbf34612fa768b1c73a0dcb38ef49b614dd47b74666014a4d53f143661a445edf4e15b944d00c869627ec8445a5772ac4520c0bb3f1c4546aaccc0e712673bd04a37e717299e0de08110a30e7e6a0c46e402c3ed3d9d195e53679466245a047560f82ce7838ecc3604ed7a3c0ffe1c41579aad3c72faf9f6b6232e91f07199f1693338696b3f9c10c8d766712290567db5a8fd5ee29a2abd9eb2a5dcb331ba15c56086215583ed953797f31690cdd1783c4ab8bb54e1c4649c01b1fb141f6a12830732477e367bb4548d52bd20398c16c8e1cd95954b12b4c48ad93a50d478104cf50dff7832179c2c3cc3eda16d2a0453460d4e91b24ee4c1e4a51e783451d19bc4355f3348708e3132f8afa15f98bf7f12029f62b1b2ee666d7c4c380c1572102dc81405864f01873af3be6241a28ff34e7a3bafdd30060ce633e59b488150375027b12b38521d859436e96942fe2e86f8a2aa0a25cb4f1c698eec7aeca2679cf20758a19d3e5d289b0014de67e96883b240850c7091d34d1cb4cd0b25cec76754371303cc841a5342b612ed4a4a74b435badae9b0c35c4a7111cee4d43b72129b286728e8ef18fe8bb6e0376fd2d6384568143ffa5b6abd9c4e490ef501a5d31700662aa461c01832b5d446a74aceedca731551bc625fccfe2a07d3915dfa5411d939cfc71dbc10ce187b7f0e982d7bac0185c96b9c3d3f5a57ec04d9a04cf3031c4df23ac9cb7ce625d66024ced0bde4f58ba5a24152b3b62485736f3f3502738ada30903022ca7439d777e126b317f984ec7a7c173f06bd68fbf83a762ae475be9802d24312e6877e1944b1cfce55158851b7a790b12edd9c8b7f53832a5c0f7f591adbd0a5d89e618e7e3bdbd9b94eb9f91abd3b9e64b953bc29a8b2e53b0aff1f7fc6893b6a48db35d1adcef55ec5162d21266a0f514c388ab92e8d67dfebb63825b082b90689868877a3c68bbb22bce0d3ea5099a3767489bfe06e0f785c59c3806855a0d546ade91a73345125a844741b68cfe5124d8e0db227d419db1735e2e0bd7db94d998106e6e91c69d4539640ebabcc851870de8465563d780cfcb6aba69c061cfdce350802be0ea2f65953ac3462c9dd5d062a0d00174e45fcd464c5882102bf5b11c75e07a977070e6c9ee0cd0cfcde057e7908211f3d1018d4e601685f76cbe68fb7d02ee0939897ce42fbb2799eff8f30ea6eea1e6bc7865ba9fd1f30b5f20612b8428a10b923802818806128fee788b14652b21d1523eb203fd3ca6cf046f8b9612948f033e7ac05583f738599659ac09f59659a30c97ef16f0c689abf9f821f569509b4029ae66b02cd333381b9c0344d0e66d94f0b88656655993fd35c33cdabaf5f9759f533adaaccf8a7c004feca4c20f0f7abfa5599f1ef678a6555fd4cd35c33c97e5a5840b32aabca34cdf734216848c001b8c1ca049a09cc2a086682046b0a604428332b5499bfdfd50feb9b412f548109d3fc993fd334d33a564f76193bcc4e3136f53386bf140806a4499a5e4c8864a68596479dc50445dd4e8ba06fe701f53a2fe70a1da45b059da7d1690e922f93d3c286808354f6e904a431348604412df60dc6bc901898bd13b44c179279cabd4b25807ba2ea698f4a95a13d0af343bbfb726fc8826ad870a2719270772e90708271f52c0f64f43bab26fdacfad13bb0f7fea2c305f15ceab872f8eb9b2e22970d1c74f3cdb481987ba692c10253419f8d5f293b37f8f6bade9d0ebd2b85c81bcf2b6bab395143469cfe80423ac08807a33ca2eae49c3df0497102eb53871f40a7a26bd06104a117645eae59193c369a0b04d4402e10059103402790c93c06e2f07079a90d4194088bd2004243604c11e2307bffc13520e102f0cbba234c8e1f348aee5431ca22042894a60af92435075ef029a0232a0984513e981d820a60032048815204468a0348d8819a0e0d0966ef559bd28e22600421c3a947efbd829007ee3abd7bb233aa77658edbd163878a9d2676f2ecdcf50ce7d979164f9c439fd22b3d913e7dde37db016147cdce063b64777745239ee1f4d0e13cf3d0e14416f1fe2c432814c2d0c4a8f773ad0e9c3a5cea3ce9e79ad6b996dbdf789e5a676e7fe31dfe8974ee8f1e2678f0d0bb8915ac435787edfdc48b27dfaf911fee2d78c0157f1bed615fb1563b2e911e3efc3ebeab1367562c8274e220953fcf7d524d99e3326068a165d03a3bcb71e6d042d3ba719e6b27da79c3fcf56e9a74420885e8f9e144d9b3c389e745bc627d23c5040da081579cd58e4ba4d6da991b10c280b16e9c7dbe3c1a60f673adfe730ae5ad54ca7bb752798f00fa3974bd1b8dfe88abd55fe98973bad1e7c81b2f2a7d9af2e34b398c7aef7df3f9e51820274c3f737fe6fec4e51039f97efde5d8c9693b95b77d7e3ca457f639d1fb8fd34fd2cff8737f2b870014e856a120f53e05001eddc18810dd1492a5770cc1de81977aefb5c980d9c8647436c48b427b394f207356d7be4c86b37fbc717c892c1e85f5d9bcc48539d28760606a9dc990ba67870e677a7676d860668ecead845bdbb084dbdcb0858814a7870fa7bd9cc7189362d0f3c3892f91c501d0f3c39923fd5158e7e8bdf7e8c13797fa122f2af1d4faf0d4e85fabb9d1563b2e916f550e3201400709c7d15ea236602801c21639592329c2001d808a0aad6057ac20792382051ea004cf8ada8cb6544bd0805ee84c98140248a6519a19f0088f3614336c800114c41c0326c99c9d89660ea032985dc03a4bc12aa5b4585970c55c00e45025d88552034dd20c6615f00c248153f00b0b2068a6805747c628e1d42684153150bf4c71f4dedec074690b6afa052a550547a22e60da3a4cfab5099348d05c0c496a0060f4ab12290b1b465224a06b857e3da67cbdf87463c29ca33e3fbbcff9a70aa1187c5eeefcde8898fa04f600051191e4fc52378ce8ccd13560c09f35bf2f08f004a0e432054b0c32a6292f55ecb08b622416ba9890182081650e952c434956cc3c5134ac891181942c6aa09864f4b47309e80140860724a61321308438815125013b40ccbd357493b4f43582b087c38cf14002be1b6b72e40531476c3972a11200a7bd134613832273532d964c82c964e0244511ed4b044b2d7a7fe08e0c3252236a9005d03ba21f79b294888df98b7a9f8386810f23d153264cef5b1ca0b7a485ee94c2d03b112c2a305c000b8082dcfb7181417097310df020f63eb74487fae706a618c9dee103a4fcb9b107cb4b4def653d40c47ca3a02400d23bb00c5fa604c8cc7d41aa4b970a7909f6aa547fac8430954a8d2037602d22857d3450a9a82091d327c11d47a48fa6543ae450caaa9d6c89659a948a3e3584cd033fba020d18a57213636646a5883b2154289570528ce01d0d1b385cf549d5450544243c816eec20730715293f682cd4fd346d2307952323599d46d171e095b706d59d02da924f9684a902880caa01460d30044424cf01a50baa255f1ccdc1f286d29b255650e58e48ed992533e8980305d50b00e082206902e7421d125420f6c84975c7d641e780a0b230641e5a19719c38820395161b3665c28849c5e34d7e8ad3a748a39e308080a70bc6530b701963b760305404417d0ace223246b31cb31720314e65799287e54f1c0b7b00a03b152481466d18c408f981f3762a0f889e428e06e101c1a1ed541c01305e290d2d014a3a9c6a39b15a53c03490a1c49e8a904508203f52e848018576aa1fca83f0b1e0632d8f99a94a4c00c074c326a6a81833c5c74e108a9ba294dddb652a8d8f0e5c7d32dafc61b1c0140066fab83509c137774c5565ea06262b14f62a6c3ac2c794297fa506992565beec6103ca1409422b629ca57501fcd0640a2d4920096598ec30d56192a92d03ec9a701291676801a35210a4da4b63ca00398c54142ac5684c1215589e00f028d07befa8fa94aa53490c10d08398ec280199045cd484e87fa9cc71a5b0be8f2888383d3f9c4ae592d6c087da69881c9748b5fc68df69c894f7ec7befa67aefa57aef11f4de49f5de47752bb96ed5260a8250bd7750bdf74fbdf7077aef9e7aef9d7aef9c7aef9b8497d0b405d19838d7f01ab9bcf3615e3426f1d3438713570a63619ae640ef5d53ef3d53ef1d53efbd817c79f7f93732e60167de3a84a0993e5f6fb8e79566d0f44efba4a72df5de2b51d211342376b46fe45917ebf3e8bf32ef334eeabd9b6bcc36c13266c13256643af5de7b97d47b8fd47b87f4a807c998f52099b11e24f3a067c57598b095a669bf95ff45df08e45dc48b7ac5b6badb46bab5fd4fe2872a3846ac587f5a7993a0ab7bab7f8d2cd2f89773fb72732352d49f887be31c8bfae7bea8cc7df2cbd575fee772fc7ecbf1655bfdf7ee1a298bc11a20ad8769e466347ab630797a1fd2e8977551af58238b7c3e85b098e35f79dbb53ce123ef628df4feb48d1fbfdbceb58f2c1acfe39de7f788b3782a97f9c7ebf3477c79f7cddd6da7f2a6e18fa73f93cb13f993f75a1e5e549ec8bdbe33f7e78d9f77adfc7a7fef36710aebff954bfae3c8e2ddf9edf37be43f919e46bb95dfc6a9a1cf68f45fa9c1763b27a890935f9c70224cd09d72329d0859aec4298f38e9e6ea529c42e44fde27dc79a796b6b19aaa198dcbe4ddf9ffdc295cde7adef6f45716eb901e0df9e790f789e7d1b7b10f29e214d6c5bbbbe55d9fd22b73e44dd7451c0f791bfddddd89a3e1b67dffda2bff71c429bcbbbb71a35fde2e729ca7cf7ddb89569f681d8e45e25cd2a7f878dec3799ee78df33cbfb1983cdef48dbcbbbb3b4fa17dfb1ccf97f769df423c2f229767964359306038d7eae2100db7bff2a4c387b7f2db18226f24964158ecf47eaeddb98915c4f267e45886b3bc7b88178519729c19ce72e3d20b8527da8df3d442bb897b67b9f2e3c8e2dadacfeb4a0701bc740160cce1293d910da95cce43a3cfef7c880f8d463fbc52650555efbdef14e2bf6854fa8f8617711f77e24aa1d1f7de090deabd67f57eac927ae557a38b425418af696949fcdb28e243fbbe95c97f2ce643fb2b7bef828c43e5ad54fe3b1f2697c3de7b2040f97dcb21ef62fd65a3ff4317f37f24e645a588debb9fbc3ef80d87872ccab1f14116f18a4a15b51fd17b7fa3c01e3d792a5151d0fb9d7f7cca1ba3d1b309f18f9fc81b2fcecbbb2e7ae552041af2e3a7c78b79ca7befec331a4f7c89233ff2e337913e95cbdbc529c4f165ee953e99f4e7b2be95c9538386f31373db2ed6275ec49379f74d3c695f23ff89bc93cbf3cef3e83b4a2761edcffa1b45f42ee7c99a286fa220e9bd0b3f8df469ca1fc52eca5beffde4f347bc789e51e22863e787f35c93a398509040c1d13b9461ef7b6a4f00a83f5ea754ca792a64ba2768bf80a143cf6f64ef1d8fe9c18989ded1a4cff163316f6264a289c68b471a2f1ed598c04bdc74a371b9649513a7f70ea780de7b9737375f5e67abb8f9f23a9ffbb63cfa7ea31b0ee154b8cdc9c97643b9840839157473c3e15a124675922b1dc9a3def190457a59efdd469b2ee2dd6fa8aaae7b5eae76f3c77bee236f38ba147df41120228e40c8334206570a4db9de3b5dff659911f45f0ea3d11fe7ee9b6e441cc3f11bbb711bb3f11aab711aa3710cc3f00bbb700bb3f00aab700aa370fcc2effbba6ffbb2effaaa6ffaa26fecc2eeebba6eebb2eeeaaa6eeaa26edcc2eddbba6ddbb2eddaaa6ddaa26dccc2eccbba6ccbb2eccaaa6ccaa26cbcc2ebbbba6bbbb2ebbaaa6bbaa26bacc2eaabba6aabb2eaaaaa6aaaa26a9cc2e99bba699bb2e99aaa699aa2698cc2e88bba688bb2e88aaa688aa268efbd8b072ebd77b39b05f4310599818828fb8f936be54d7fb94f236f2411f726f2e84fcc6328632063d831e6183162508871c5e0d737edeb7fae218b3dab42cf428588c1db3515a2c2340d79e33486bf567e9c0ffd5d5de9bd0779d17b17da5f791adbe450ec7db8fad7371b9044bd7721102be5ad54ca3fd0d0198d9e0d6ee7e6844ae4de8dccf33f899f4a6fa47f7df6e5ca9fbc95c99302dce23885136ee7e6cefaff5a882c7a620916739b109f7dee6b22ee4dd41fb9fcb5f13e3b9ea7700dbf709e77ff78cfdd9d4262f244dec4bbd3be4ffc2b89f96d23ede299e1ecfcb8d193388577eaba3c7fb22fbf3b73bf54d29db83f7ae41dfd93f8ddf995773e770a73fc3612f333bf73e44d23efeca21bda07ba0eca9b441e7d1b479cdd9dc41cffca9f7f1b593ce245a427d2ff78e2218b5fef1b8b463a2d27e2158d277e1b4fba98e345ba9814f181ae43879cfe7aa7cf70769efeee035d07fc68341673fcfcc8fb1f6fdcbed3f2e57dde78677e23f9ee11a7b04f791fff310d593c3f9ed21b4f7f7e3d4fb4bbe36c64f1567ebd7fdbc853782a7d12ed149a3885777773a730438e6348fae3d9899f42fcdbf799f429d0f06ea3cf802c9ee146e327b23885b8b0cd8d3e977609bd40f5af717220e29e585be9bd9b73fac00430a52f481f5fb0af461fb16fae6f455f5c4fde67060b67e79ada99c1c28966e15c4bde279a85f3ec3cd7d0f0f39401e3d737dd47d6073cd7329cf9b3fe725ff256e6c83f54e6c8e21a1f1dbe137c6bc3b53d48bd1e1a8dc5bce170987f7c98f49f38cc3f3eec41bd167a67fd5c5b8e3ad79672fab9b62472aec59d6b4b08e75afea8f7732d47d3cfb51c47efe79a8c1eccbb7046e71a9ea5f7730d379e6b431c3575ae29ef9c6bb8ef41658bdebbd2ec5c0bd67883d7e071aec1e8c11a63693d989c144ca2e9fd5c337924ef7af036d5fbb7ede59c2fe77ff44a9f3c6fe41af22ee2b51f11e25e6da4a5debb100f792bf7ce7fbc711cf72ce2e6900e8e7d7a4fab913e2d477ebd5fc178cc89c3a17279f7a9e1bf2e2ef1d470faae8779353e54fe1cf747b5bc1a4fcbefdae719efa4da5d140a7fed8f1fe973e59d3c164ffc78e63840effb7939fe739f477b258af32e03da997d0f89b827aeadb9ad7aefd1fd3c6f1c81872c26933ec529ec33e645ba6844def589d34562d22bf1ce4fcc4fe5efcb3f3167710a9377f2c48bc2a43f2a3d72791a8d224ee17de2ca1b577ee4b23ef1e392a6bd1b1ef23efaafa43bf34cf47eaee115f1e110cf4bf9721a7dfd5c23e29e581475ae21bff1c46fbe7c99b3f560b105ee89a7f2274fbdd3b315cb7a17aead09cfbb9e2c12f1fa97e8bd1be9bd9bbdd744b7fbdcd77f382c2287872cf6de89f4de4d9ffbb62175d1c7fd4f028f45efc725f29f77421c4f168bc6e4d1ffbdbbbb33795ccea747bbe74e218e7b22be3cffdeddddddc921c38693c3a9e1dc70fe6f0fcf4e0f67b2980ff36fe4902159cccf68a3a7871ece33ff4612a79ada8996e34be5dddddd29ec9b4fb40cc9627edec8b333adcfa8f4c6e2b1b854a6e5c85b69dfc1dd9dfec491771179278bf9e9e7fbbcf3f3eeae2f2f2aefa2327987df378564315716f5289c3f5f268f377d67f42aee4404e92a73f4d71be6f9bfb7b6369ce96f57ff1ab9bc47e4f9cfc268f46ccbfa23ebf872bae3da1a8ebc8b38f2e77c7ff6129cd1f9ec5d889f68c4e279ee7db8d30b132c9dd6d686c334ffa29fcadbbe9167102b91e4b535bab8b696c473e59d828faef727bdf7183d78c7a5e34a612b44d3e0169fef944af9ee3eef329c25bd720fb95c5b93bbebbdaaf7bd8effbc6f86edbcee782b49f4def5ba7db7a0f75e0f393deabd38bc730eab20e9bde33752191cde181aefeece63d7d2bb5aefbfaf197bb46ef4de913d3883adcfd8e78d49bcd876fadefb32efbde3bd77650d197afab976d5bb5e0fca00d1fb4f1a3f71a8fcc2fad73885777ee2c5f3ce7befc9b30a13823188b852d8f7f386464fdf4e94c098c40a53619a66f45a0dc0822ffa7af0c5dd832fee7af0855c0fbe907bf045db832f2ef4e08b083df862ab075d44d0832e34f5a08b473d6897400fdabde941bb303d6877a507eda4f4a0dd911eb4dbd18376780fdae1f5a01d8bde3b56f02a410307bd83752e2fbbb507edce1eb4e3ea41bbab1eac13d583759a7ab06e811eacd3d383756d7ab08e4b0fd629e9c13a1e5bbd2f78d0bb185d971ea47bd283747c3d4867ec413a193d4877a207e9d27a90ee410fd255ddcaffa23626976d2a8879f2b7a9c89749a41b16174c70a5f0e6cb97f99f13d1bb0417183aae147ea87f325954e6f8da11f77b1df2227269b6bddbe8e3dbd35be6b8b246f2461ef368148978f6afe9d9bf70d182855d1ddd9cdc8a38b8146f284e982841426e631b2122865fb765573545d5846968a93668c87086e18205b30a1426a43a2594918d45100b8300f6952a55aade7b83bb1e648108570ad935ed2b6f3a2d899b7dfeed86ab3f91f65f2e2f1e8b6fb5f2dec3711f2722bfe188b98f8b3b2e711222e0169fa351fb1170fb713fa2f6233e9ec7f17f342eeb3b45efbd5845acc233557ce30d571b6f13c83ae4b7712b49e41fc745b0b1edddc63f42d96744aa40d6e1357249e2d35e3dbf8d226e4c2ef3d3dfcaffc2e7c6dfd686d7c6db441fd2a7b86fbadaffb6393c64b146fe249f8c74937b3373d42980d7b46fa3881f8d7e59e39f7fb651c4bfb2f76e7fe40da7024f51ff5bf95f206f38b636e44ddf746e371d5eff0f47ffe4dc3f7a38fa27e7802ee698a4c2774d4b33d2cd19bd77630fca5de9bd7be8c13908354e0ef8adfc36fc379e785e9c4fbb48c471a530a77df278d36a2a74856b1afdeb9b4eebcb8b4a753d4ffb3efbbc52e94fa488140f7af06d500fbe35e1a0a1f77e01acf7f8cd460fbeb13d8882540fa250d48328d0f4200a233d8822af0751d0f5208ab41e3c01aa074f14eabd5fad786366f5deed1e8cd3145c21872f6b7cfc4387bed95dd3d27e6efacf461c91defb79e6fe44debf1a7174c2defbaf07558c8a8bd0fb71893c3d78382e916b6b6bbd77be1e54c187ff258e17d5703522fdf3bebca8f449b5e34d0be725312fe2a9a9ade1c93c279eb94f9e1ceaaffc78784565fe954b63d1dbf6af4fe0070c3da802ee6d24e04c098bcabc0d4e4e7edf731de8f6de7f0fc2b5f5de51b8acde8535beb6862b85f527e29e885caaa54254f8ae6969c01e4c2197e243effd5cbbbb3b737ffedce7c9a55279b3f1a149ff896bc8bd1b9987ac53dec8e589f4776e3cf31bc996e34b0e45e189f427eeffec45e079f158349ec45c599c4fa43f917e26e6cbfbc48b5fb9cc97f69c01ffdfeef3c9f3ce3dd2edcc6f64ef2424f4dedf4e949dc8d27b477ee3f9e1ac8b475c29cc95379e47179735d472e58dab7dba68c495379de27f1239df8743de45bbb96f4c169574c8ff861b8d2244e0f5d72be6b447ba11716f220ef76f7b371caeae75375d8dd74811f82fe23e8ecdad76a3f1e2716d0d47febc3b5f5bc3d7d644d46e254af58ebff8cd97d7f9dcb7119325c8e0ea0b3b172e5ab0b0abababa35b3027a7fcc5defb8a4ea2eeff4c7b377b377b377bffb5e8bdfbe8419948cf3d5e51cf23bb55230fc3dcfec6616dbc819dcc7aafe2d4abce58f58ea596e3f5e9c1c399e10cf98d425a89abf99d49cf34dbf8f1d383879328fc78f57741e7c626224e04dbd9e1e4ab8b668ddefb8f1e6423d371a5b04f9923bf5ad21fbbf97bef3a7a908dace34ae1af6fa4daffca7c56c38dc69ba66fa3d1abf529bd722e1ad5727ca99c8bc61c5f2ad5e6a2510d59dc53fa3aed1b93499fc6ba445ae97dda37ce3ec7d58cf79dab259739be540ad1f3c389fcc673adfeca8fe3bef7ac1cf9f58c46cf161c711c313782c308b3115c23b44654f5de6fbe7c99871f4590e9f93279572722aef78fbcf3ba8823e99fe4fbc8e5894662ef4e0e7bef670f86882084a0deebafbc917845a3cfbd7d226f24daf9956d9d62f5c4b93c4fd31fc5cd97d7a5e8fdad77b3d7205af030c4a9c4a102f61228d57b07ba48ca8d59a02bd5a7de7b55982d03c43d88a3e7edbd9b209cc050c1a4c81539bd7773abcdac8c00b7a4b1f76e1ec9242183d2aac398defbef4d0641e9088623a27aef40af2581364999226750effd2a8028a3cc426ba842f4de7f934235c1614a8aa4b7f76e55e50e8b0a910f0d477def4e6af5de518148802b8573d1a8862cfa631a8b3b2eadeff86b4f8b50c3bbd570fa6edbbb93e7ffca4fa4130aefeeeefc3c6b64d13eeffc947187cfddc1a87faebc93c9255ed4bbcbfdf12b6f36618ecfcbb9b34fe9897745e1afefe3f9f58a4aa23fdd4ea15c4c8a301a3ddb9bf0ee7f1237ddddffb6f0ce68f46c77df46433b2a6f1b595c41ccf190df88fcc69c6f594c8af834f28613fe650de19d7d9fbf84f02fffb2c6a9fcb6f2e7f69df01b91c519af8fc57f54deb6f227718f1789b827de49fcee2ec791458fe74ffc23eda2f2eb9df7315fdeb33f7163d1e7bf3e914aff8de79d9fc6fb4e9ec8bb58dfc4dceee4cb897971efce857d79f2f84fbca8f437f2b4dd4e19776b6bca7f07a3a8fccb1a45afe795e7dddd892cea1571e427fe0c2ebcbeb4ad3c3beb8fa7fc474fc44f3c1bc5e39de34b3a4716cfaff4caa24f9e697d3fefcc50ff1cffc8b3cf7827cffc897fbca84c1e6f3ac3997d9ff8cffbe613bfbb3beb4fa497f869177334bcb8a7fcc45cc52974c3ef3ef286bbab8b77a7ed760aefea6211594ccb8dc5e58c7f65f22bebe217d61f5fdec8130daf8dbf882cfa1c79d34863dc29acbff2c48ba7f2562aff89f4271afe4fe57de6f4572aefdac6f365f2ac91de682c2a3ffeebfae37e4e9353889023717a4fbb9dc2bc889f7d483fdfc6f984533177e2f4777811ffc85fe244de388d62528430bf91c89bc51d777e628e8617cf7c993cbfb148dcbb73e47d24e645e58d67f4a7571697c933778b13c1f79167fef1943e179ecadbf679d12bf3b398f4e79df2d7c6e272049bf0ee537a3d9ffbba381412f122b248e214e637b2c68b27fe8f379d2f6fa4ef332693228a49112c54088b497f0acf3cdce7b63d77e27d1f85b098f473a7f027fb36ec79255e34feb6e1104f983ceb5fdf34f29ff29b08b6b9b3feb947fedcd3c89b3ebf72997fb95388872c2a73fcbc83ed7c7977faf3cfbf98178d1ea9fc39df3f7d51efac3f7e27f9fed9f7f38879714f58ff8f9f7d46a3bfcff3b46f7f9638ebfff11cbdf7aade7bd630477ebd9b2f5f0ef16487b5357cf88f5e69df7c79dd8713edf873bc6fae83f22661347ab66f9cbdd1e8d9d6d6b4d6d6d6d686c89b1ee2bf1e227f5d1789c362d20f93f8da9a6dfcf828e532f748bc227ed645a3d743ba9d21eafd56fe1767617a471a913f899f1139239ec1bdf74f238bf6afcfb27aaf3fbe24d27e410f62e8a4a00731202a9e7d79f2286340d33b314ffe9acfa7e8bd3be8410c53fade6db4955fefc6903cefeef0620a0c71bd2bcffce3dd70bdf7063d8801eefdeea6bbfbf4d293c090759747dfc6117767be9c8bca1c2fd667adf47b5faef7ced5831732f5dee95b993cf3e57daa68415777c14def49356d4de4c9a292c6f18ab8dbaa267264f1be3fe4cb24d22d5f2691c5146d355d34cecbb8b5350ba37aeff92722dd2c0cea5df8f1de3b580f5a10c0c2dd4ddc2bfa24dafff6791e6b9f2ce677f67da2dd2d73dadf9d2258fc6f0f8dbe982b7d12c7bd91ed1bffb787c3fafbffede1c7fdfce14387ff49b87540dec53ca9e27f7b88bc8bf8d248238bc962fe8f778ab535babd1b59a1810a892a00e9bdbff56005b8f7cea0072b6c98d08315b4da5098d31ea49052009b90678293091ebc7a70c259ef42e15dfef1a1f21b6ba427fe21f2be3bcf3c9fafadd5d5c5b63b7ce9e93fd3bfbee9b37379f45f79a6675a06dce8eff23f478754719723fff11b93c7e2d97997213d537177a6b19d152a08f17be9897cffa4f9bed2eff9f38f388578fef1cebc384c313cf1e289e3de480f3ff2861bcab8abefa2f2c71d97c722b298626d8ded0e46df8cbc4f7c7917f5909e989f4abce4d1e7f8f7390624ee8968e73fe922d227dd4e611e1d604238861ae5e920c4108ea1981a766422d7113d632661a1b16c9d0aabc3f0e78a03646472126b53b0d40d400955b720c79e1b60733208d263238c6c8818198db8482934739d8ad3202e55e2c4a04d69748cf5e9b1299bef485a324ab9b522c7a53d79cee6707aa02c0eb13d13339afa8c18f0c413d99740b6f492b42296c6468c2d54d39a5286204c9a41f6098dd70b3d6e605ed8895263e041039326d9f9d46622cb9a2613b21324044860a40559f5e628a2cf20a1280b938d3661395278ba72c7026fd581a58bd0263d4ebaea0f2f26a5e07a130a1525316c168e22827169a4380f619e884a553601d00a33562cac011a61a64268a2830a002b3a2ec50e74411a42a46b8f1f23abcaf5c5475329b3e01aa3b111f243d619a845f2dd490f26313825baa2e4401934a07392a5466c51d0cb0fd26b74ea00ad1a8d44645962e24c88ad080d12d05153d1022a8d9902c4d1458f30434796026b4b8a437a5431c327d259f7a29466d1a2470967d09f2f0bf7a5b36f01c1a0664d16187ac2746100880b213cfad895174d742b1e068ab4d457a1baf4fe95e7a6135b0c016adc581367d524aef72d4e785c00ca812289292621aaf210bd8f11600802087af824a5a9a192054eeffd2b14df9d4759eed81890a1841a3d322278c5c22a89e24317c7c8d27b27a33378422046173a630a08e5c17c0c0165e504994da19486355c7f0869f8d292a6ccc74c8409d81b3616c508353090221e5ca400f59d91da14f14823bc324f0a1f1b862cb95416a80a264438807c882a8580f0451205127bacdc9509f2e00d1b3656c82f3506544948c28948161a33b93d593eb888b320696051211ee9a3082a1d9a80430c064b6a12b87aa044c212d598840a886929445719506e54205bc0110d68673f2650c4f8d8fa125d5973a6fdf2403a01018f602740572916702236c950244e715e67c8f8d82009d162d25d130f11ea1cf1e0f4b02c8220cab28540dffb88c7585ea80bec979c3b654c502b94428841d96242ef125796e149e6e7831d02c215a89701e1d121d0e80f88740921c27aef5dbdf7de7befbdf7de7b0ff6de7befbdf7de7bef9d6bcc1712048c64e82468410a050b648a16b03a466aa04a1cdac4e7cc2b80e9c50005c836cc71f0e4c20f261d55ac82f87020c4c1c5a1cb4a0fd149c09253125ec32d4783af3d89ae0a293973a436301697a20aa31563385498289246cd572a2e74ed7f84fe969f3449ec1c38d834c453562647cb1d095f22c83a8d7142a801516ebc7cc953a0c51041d0371ada233269e96549f04a8e9d1a5bb3a3fa4a8b8d923d43fa6854b5a48678c591e2012bc9fcc08d0658dc09c2b6772982861c0bdc9271ce88492ac3e60b94ba48c10894602853699922f9e7ad0991313666bce2340d953bae2c7e4c476c305590142636440759aa37606e7d040b2e3599458e8250cc0ca243a841922c19f4f094694a90d75580a221779e4169d65a68ef0b180578c22eadc880a2003c734e84d8138549dc9f37c1542d0010dd980b47cc71ca86da63edf2b083c896a1025ba4df151c88e84ce011f6e668c4dc276397a21f6d8c1a6c6ac091842c605c973a9510db32fb7ac025a60b8b07771ae40902002d79a24d123e99c47c7103810dd75502a5dc209151a71f2a4589abcb22d911d9b3e1e5cee778509265ad4b8e0729f4bc4e9001f3c34040a2cfcceccb1f0827a100aeb01004e2c18a08ec9816c8475986ac1e6d65125082a8ff2133d1d7b6852b919738053455e92bd1790c4a8142814a604c1a743905c60e8b290768b97a24c01c2c6439179c872d021cd0d1698f8929788e575cc423c94a1b1c14baed72f819e2a83203fb994921810da13d47d0c842b88161953686808b47785c4cc471409419134a291ebead0820a085c0c36099c3d153b404c498822f3e01de38514c06096aa0abc9da405283b1af0412b0d02eb184d02ed8ec185445d096120e555e11453335a8206cdd92b4d05e9e14f061b585c0f1d12901bcf8302a222702d203052176ac9b0b0f56553e1022b361cbac52fb24d4a33287f054e881d586a28a1752829d519f5a244ae1868c77f39ad1436c0f18356e5035075a2c48e9abf2a20d85a0015981e4749015efb8d141c6849066812e078e54e19175b9d3529ac1c29cc1c009b1330712e041894128b6196c34d56913a551d005942ac92567059c8890aa21c54526a6eb2665e78d80072c59d182ea1465e7c52becc828d591a3335a7e28906380160dee85844d10809072a84a914973c81ef8d8f39d1e847261c9804a755c21042a5a6ae48a0be10134de4c961a3fe0a831c08d0707da7e48508b6143cc44924903dc99f90c8851ba3cc841a54fdcb18599061819539d79194320c8e8206a226614759923f2e181be3597caf2102a3c8851d424059106dc190814283cc80a930a31a22780407e448db4dbcb0e4c81411e2ea5d9a803a4ef09033e05148d219b23c4cf0c27aa41498aa840864e35b1e302200f21831e62494d47049184ccb06108acc3565c2ea7f8a8d48c02aa608020ec089902af2e4d320171b1e6124d4f9fb06ce2bae0809045abeae6ce6c9a2fcc16ea0b4d984b4d52a5fa6c6cf41011e48aee81b1188428b4d9910a048544139a542a8149b80507188a0007a9862cf3ac29a5a6e901195f67403e953d49559c8bb7c4a52e09414520e08b5a2538155b982c2d3d3622ada5d95201a6112749588cb8595dcd79f3539ff1e2d264813c46438c8b48983066e973144558a077020fa1014c7081291aa2a73bb0226869cf26396d6e7e14c05d82d2a84399f39a2087465e5d8c107014b6c0e90a51009e188d0d41ae34a889f3635010a9c459aa13f40163ada0080d9707050e260c073149d4b5c5532a4d82627cf54931a80137795a07617cccecb864a5666c872fd1c542013fb9a084140e3c0197780264a50f1e294fb4e45d18ce3907daf04d6c3f528df2c0574450187a8978290a93060ad5a92078334446969083c20f0a704299b9e900f1e9d10216760fccb44d42ad49e496d964699411ab0d0010a8d88100a7e000880b1a5a201892a00b87b203d6350e303df9770227f2b33e0c4e3c80cfb8220f5891f017c111a1b283081c84abb39acccd8601841c050d741cd198cbb008c871e8c8034f0e2e8861731f6a8b80eb0e20255fda68d21b804f439a5fe9c818981e50195783813267a89541e72e4d12d91c10b8493904f511f9d861a306c731b8e8d434707fb66482b4c3451a0f3f2a28fa6a00848c2d3e507c49e4082543224953a0312e4b5a05141860c2c4ce9459a7b14b555b95e21e1742f1117be1ca90f2c5b505fc1ae6819454e3e151270440995a044014b608a20a48d4e711031768a01ee8c085630dca968d4267e7822c1672c2c0fe91245dae78252aa29e39d03a8e9120e3c3a61a0cee4450914907015c7e12785962958861b082dddafaa4a686e2000b65d0f048a4c1845c1d2fbb3b82526568d4c2c6b438c815e1d51023a38054019562ac39a2f7ccc96d9929826386e0e85223d5858f00320a3d3891255097a1228c300ea318a00a12ca0d02995b95d802601a474620d8d2814159658659153958c2878a0d6d1229b00911182d6286202de872314782a00b130f6294082ae29150810bbbe3545993624d0a2f3548a60238d1d922818f181f5afc84bb173b30d861f2ce1879e0e844932e8cd6308297f065c9c096a20d8443646e8307911e905063a7678532c14cc282493f9a4c513405c21d9a8d107d198e0f880d06ada52282c54187087d88280a9804610497903d7468e021a480021d1f33c2c82893937286434de61be227d33d432097737d12a02fc5952379748fd458b892e85311303442c0b4546d22a10182234e5671c93c322b60ae142191e1d01a2c6098f074412284abc7253b4b3e8a2e0492847c40ce854385c028cc9f1e7c088426c8d889337968123d63984a9d41d40008a32fb40c18b2a9346b5c5c8d52584af020812554622e14ccd994c09714285aeef8b0e8e34f65e104578686de9887be3d6f50a5d1b19123c1060a2d1278b0f5c558f640004652c828b10027d4de0db4af20968529429ecc9192e64c06021670212a0294ac4da8c2c0644e0920064a821134cbad0e41a20c33543096f90884e8491b336e5e47be44d0e693b1c4850a411abce9e26291d8262605d4684ab4e044c696b3fd24e5820b88e10c6c429312850f9d4f3c42d8ab97960f1a1ac0cd9c1b1fce74ba8bdb7180d81ba9ba057416c8286a0043135f8d054d301961010c6ba420c932410ae7f341d29f2e241414a8a165cead05e30d6440032b76c2ce6cc8d1400b4d813e0f58dd71c009db0341063784677860a03a008cad4ad70d1a7b5dc144a40810f500aa2459578f4401c85e615e413e096c3819b0c0ccc596b86ee8858417740060e0925766495a526b92a9c0d38329792344782abcf5e1b0affef000b3074187132f385164244859b483888f53105d99006e005bd848c19063d090c69601ac06784021d2e9258d0e3e335c6e6cf1320017236099020803888c812f456f2f8ea8e85a6024abd463ee47a9ce0a1347358414123918c28526480812a0ba1e8a8e7c91b3409e01d880c5f0ca52c4a621d20a2113a382180a05b2550c459050a80028d9608734c022a5810b44272426207d3c0019ef0a11ef9482f3054893112f16a4400296e84be00382b501c67e582a03873c95a82d8c050a496af6cc6d0d4af6596135038c024b0c2d41c6c928f4556241aa4fa2cd8a396b9fd56dd2d22d02a240203e4ca12ba1f1a013624c86932a5b783e300c91f0e15e14688fc8169c1b5abceacc6988961a281874270a8daf0c624522a4716420700a52926709e74e15357e433095971815503e487518000a1c48716371bc3e28f2216a7071738410a906a874a980494c0913524c942cf082d433aaee0cd02605a9885589a0451e4bdf43950f3cb23a38e8a2e3478cda440ad28724bf06a7168a07619ce2276e34bc71a1041dd124cc82468782ed0f537236d4bca318c8502541961698ae11934ae185dd5989f25a63f239c1202e6c4e80b5108ede60e58b26fc3e8c31e922214c103d82c0be2490db6a60841f2e6bfc3cd83377c587993c09104291ee3940c88a0e214e1820925ef4b9314186aa030a865288324c4ebe8ca5102c8859c8c1a7832006983c90ab0246900067c05016482c57c00d6210308c6882e7ea052353c71324f2fc0805a92c7de0be433c41e140c40aa2befa930b2e06c89891d64060ca4b3b63a17004010218002c7e306636ce1f1f708a941962808cec86263b0facd5c164a74271838d248a17ae234eea505d805a80dd84382758a32160598146a5f0606907de1831192e91156a608c8f957b97c40f45000065cd6ce144852d549244986a1163111154639de6b080b3fbd26185eb0632e7caf3d0054d53990999c6cad8c0c44e6c815d316000634044c3de72d2c0a624f284195acf191b044b23787889ea42634191462e4cc151e71c4d787046582601b2840c1d9a1b970cb864a8011697b632f02913f159b329458dbd15425618f2e4851b5ac2e4c3960c8ffc2c60c152a5b7326281faac39dc89352ecc13b777e40851d0ce8e2c676836a17193e54f902b1b3259eafa248188da48430c4a457e18aaf42a411253b3440304941c5459a61168128092854a5046a72d7872f640783297c0cc0d1c93eeba96463ee6985233de60e8232616838c2fcc499e5376be2270ed94ce99e593992109a2fa4ea956de2090ca00e1a53d6a9b143c8880cc07105a3ed9d861a8d18a1c7340e581408a450a4046922e24f481e2a5515a0d5212de3213d88e0b6e3c3a0500a2e3fbb3f7c0471f5601330c096439546802143e3db420d03b44982a93ba49c0604f0652a87c8a321c43ae1b887498f159c121e7cb412acb030671f292e0c1194ac595c0191f3839556082349048d6a180c480437a4a5414f2acc812c8112df094a7b629099d833963acf070552962657d453a150a025d1143462459011bc1664281b12a7394338a64ded2acfc20e32a24668a8b1d327f20a8021bc6402e5c1bee306954c90519d3851d25557e6c21f1d5860d1baf61273013cdaa8ea88d201aab690a2fdc681ab3330602d5a05365946c193027141f019f9a905a0126151b0348fabe8447a5214768f4f6be6ca5ea32a42625029c2c4048a45881e2b504001d8a14c4c1b14527f770c3401d4649fa0c7060a2449dd06b00bb48160c29e7d0204a7b22e642435f8407640238f3d6e0d29506b0a8479c9660414d281e95595264c57c2629004d510650f3c2300311a24f795ee0a819eb0126ce031b6235dc250e50124b320c256dd91a00918ba0103b61451d68617d8b36c928b430c05d6194581068e4460222b9060c5de993e62a6cd092126b1630b3f407afcb9b0ca2518960052a480459bd41481dcf99312530bd75a1ede8644502c802612146c100206c92a3b0178ba6ab2917b4864d5ce58d2c33c02404196229eb831a12514b200ec4882caf3c509377fa171abd111b40592419c5cd07842c72683a92512e4c9803b4a10f9c2a92caf4d86e045800e71e208c1d08e8343062d1030f21543b7940055120419fa2d7d39e3d1016300181f77902d3e108a2b8043675e050e6608d8ab816c82d0566fed4a28a7c4e70ed0994c6c9dba60098142152896d91d0000c475b230812f03ab5e26cf201d2892f83c860d879e57a30707821b25142e54a9c102e175e7e887b48a559f235face9f1cdd17271821ec98daebf3b19f43c45fa506c6c6a8c14f71a2b02880888814238059b86c614a75624a9d073b42a1ce02b98d68b3bf3e1db20f34909021f2005e05c3c9023c22a4124e5262a576688c9a8304947b461c6dedc8018708010f951438555c8467a44d1f2f4731b62801c5e02e879c3b72228ef58e082c563e6a9ae09b2b1b42d982f4cc1160eb8bc78c5466c4e03387be3b3632bd1f5e314e3d3a01d405d71460f1666e522133313567506e1e98e8dc7038f085c84002b4e01952311240e3242fb3a98b88692401395d8b44a91135e6662b51918a18d381bd0d625ca1b2809234c9908a3bbdcb07b150a8208a4440a834b8ad3c29e2b140508f20625d330051a223a79507b9e20c5a41aa7417a6f7c391da85247d6e94885d582f86a324143233144127f490951976c212a0a220783049ca550c436542eac489557831208008203492930794822f674a0d585c960845bc91e0b58194a7064b0850b2a6081225a941171e1c39e07007080344055d40541e46155a4030610446dbdc1ddb8e1c0a960510421396bd20f3801711a61bc6809b3b2b212ae61a45062c85afc0c6a182e61329b4088080b539447616603431f1204345f1c516c8ba209944b494e7f74fb31cad5d09249710440dea3061e477b05f7f44d469f20276a684ab46a8077d8eccb11e82c6e0fd197b208d9406ca162decead05b487c89a8c305660655116bef756106e43c3f0d847d5d09c000a05b9d15308f97213c75e8e1986c1e81441043a2edcda237be1a72dead31e72465931379983e1571c0a784d7962faf06c896b457ae28a1c99b3300c2220511aad0f5276095092c6342ceca60d9f2b325a24c0218a44865e81a8412197fe49d5e4dfe97468a60b490036702509e2ef225b150748251e21e1066f28b8005246131071349a121434e8a8c39b2b34873a3ec04f07270aa0d14180f2c9849b8d825088cb2d89c290052410b7a086ab404121716cb2e98e768e4880218411534f43970d58100d0d5e44a9d5da61509e48582402cc1c34b9cb0365562c87cb9e0e27960860f1777803404b7ec601a63e950a40e2c6e6ced31ea71a3eb6bf56207c98f223e47170b2c40484ba2b008072821138088153a4ca04181c284111512663404b038409097468a18cd28132323e9c11533b71ea21f5862c2ec81332810990610828829a053c2654f14395319860039b455f6e54e490602dee2440026ceda80bdc0d0030ca65a28727087c21166815ec3919e2b304fa5eee013140f2c22a41542090b3858649b6c9c28f4a68c93933c7d02a571204c112e360b90773e3da88b23e7d1ef668bd881cd2a8ce8a921daa0810c04a00273cc18b23549c70771feb4a1589401fdd8f98ac2d5f6c70cadee0e5201764a3b7754e0cabe0c99655e1a05886120e340398a8e162f63477c400822e3dda591f240451b4e461a6c1d4a55cca1b8b3824465dc1a54d7c6edebed1497b33218012306607d91c854615184471e143c60010fa8e391c822445e340dda325ec0b0c7c24a8b3bf5b2081ede1aa0571eb53475693f90a17238c954478496aa0870a92b62f8269d208091a82f699c2490b78057461839a2c4cc1905c4f4084bc97cbcda803c49132a5fc090a3c5c61a1cb00f6251bcb81965a341a707c6ec41b487788541f0ca1027a5dedceca48c99812107a4f9aceb4e037b7024c020e30180368d0c1770080883a0c20261c0a852de4a2140b6064d86613036f48b107708c084ccc5034c36852dea32810255263b5884b06668ee364316b9b01733507d515302af4e1aa323bf3c0b4f021a080c88bb0b1391a66fae0c025b5a3c1efc09c07da083833d0d32157aa382e90b13087642dc31c3e7411cb92f7b8607fc38b6ec8140a114e540240643dc0d3768747411f180851d2a619480a1d1c2490ab93f2a2e0c00214d4d082e0256b46d6af3084c8f489de6cae23308368312001aea2982661864885c92d66835a8a22287221d8e7c34ea435107cc8a96054e465490b1a24c0a0de5900069c87eec51d1661019d68beda1830d064264702965081ac436268ba56125b98f093c02b006a148902804409109afa343205e30213536880029888ac8d9500c72c88f83b47db9806704b064e75565c2a72f8f32e8a8c3f605a48a102d1baa9898924ca6ac0cd4783266c10ed14daf879dbf608d01210c3118b525854a8f344c5b613b307482e052103d71bc4eed80d457471932e0c901760b98354c1170384a74680e1c3786cc107bf05991a0cb9650c1a50b86a0105029cd5c2181eb0b96579f92482892d811963722bcd8875410113bf4400072ce986031e48b5c83039254a011c4232a0b1b0792b86bf27c72835bb814ba46b1212289d8098827d529821024db18313752a63136342f36f88c1488090a01109c530cf23440294bc6236c517a5c9931d065035209335e61540290c1ba32d89303eb2e61543a76356d4db8ad6ac26dd5ba0b947ef461a59fe743295daa55a19a0ad1b4b4af57cc8bc7a232f9d9cc32c149a67afdcb3389c9241b935cf47e7ae8709e6b77cabb33c77d52aff6aa9d88d4eecb8581d574294942efbf8dc68b29fe27b17a22eeb66a5d5b1ff7eee4b036de44fc2b897491da9e168484a6e34a6131e9873ff77a4565723854f16e23a418777a7977b5b1a8fcc9decdaedecd07bd9b5cbd77f391a9130f59a495f799213feb9fc6879ec4fc9ecf4e1f7cb4e851a24781649c1cce1ebd09127cf4e4118e33f727dab9a6fc39edf93ef1c48b7c680cf70f6caa96c151ad9e1a369ce9a9f759219af66be5c7d38cc963f1b6916e24e8d6d64c158d3acde14a738e6d6d4d5d971f943f0e1f2a7f92cf1f713ff3f9e3870e7cfea8aecb0e7cfec8c8aef7ff952496b9cf6f1be9d6e7e31641585465f46bb85268f44145422186fc1b8d46cf626ded86c3bf0be5ad54ca0c0625aabb5e8f11051a2a7f5e1cd23f89abd15f0f511bbccfc7fd6f0ff13e445987e80e19c293a51b5a508881aaaaaa2a20f0f7334d060e80118006f0a84ac2a3eaed4ac3696285ea11fcc2c3ca6a01d605453daab4a455600bba8cb932c2a3ea2dc197d618841f3c12b8a87a012c53905f2dc82291055f6901397835e291f58647578c1e095ea0000f2480c4c32ad843cc108f05390503782480d0e087c5c14acd0a0a112f055f01f8a842c2c3c104071380695b6a5910b652f9b0da90055b7d003a0076c802c52355bbd2ba22c303a8e04aac0a4d410a34ab1654a16991b0da80e5e24aec4aeb8ac682ae055d092024f8c272013ce5e34a0c88c687951e1e1668541dcfa105b2aab5050eaeb6aa4c601a90ad2a6e01d8d50f8b0556042c382baf06e69502a055d5022010f8036201b38009b4c6aa805875585c0ee2b4b6b0aeb04c2056155ba55665668501c77a6449b01ac3a301991e5b575511b2da7c5439f2416178f654a82c25422428d01e3d4bbcb9264ce411203cbc3792d326350c41075958305ea8804bf5a7d020407f242c41a74809d103707408501e3a6ece8051135e0060992fc195a13c7562b440a104d8ecd03123c68b162ae04a90905006f625274e8a336674e418928ddd48090d1c3150e3c823152a5110c48001f6b575280f9e3a74dcb449b3444991e10c172c54c0b901fa83c7cf9c29261629029427013a067ce97224c80f4f9d0e2d40c700390400e1f16b37798b41274a931ced49200e9c35033449f2ebe18c183058b8c91cd6326c3100f467cf952a4d80fc7ac609371232874e6912a03f0ae08903674d9a019c280122e3849b070e6b2f9b66e0daaaeaa9284d6240813d7812880327cd004e9a28490224d250460c106a4253a5de503868d0688c0992232d58a05b921ce9e32b93201510d5ef9455a95f045963aac4fc06a80263aabfb40a35850ad2801b80e79784df9819014b0c186642a80233bf123ce0e20206ab1c2c68106420b6204cc198d65602acac2bacdfd5959515d0ec574022e08195e06a0c0bb6d202922da0ebf1e15785967576a59505572d7060562db81a56ed011900b7aa10fd0880c755831f56822a340664b0191e5a760bea2ab4063d7e6c04abba2af3aa831d1e1ededab67efcaab4aade3288e2f1a88759b5c1100f0d817a98566b5b1242f4f8558901d392e6840f561b16e0717525210e889783114180257a0ce1119680c582ae055d36487825b3702d087874c81394785bd3e32a021e774b825809800cacf2aa8a20e99155d5e08acc74b0f6b0b4452b83212059a3a434f829b00ab1851557a1f830f0f05843d3e381d5991609ad0b5731aa3460c53ef008592b2080123eacaa2cd1fa0aa24018d06f46eef050b0b4abfc60a03a30ebc8801cb0c2dc1e5a58f195565603e0eb560248a645e24aab0185363cb4b0dc18cc6599882093d04a6062c15cdda1001e605568635d575a576b0f185c69299029ccf8c2432bcb44d94cf6cb72a1c00a18b432af565c695971b8d2f061848265020c5926aeb4b0c218b42530ab824b00d6357641ec4b4316da0b0cca05e2f1cb22035eb8d2ca1262ad0cd6d72d0e6b41159698d51ad04db0a00ac319022b019656959a158a2a388469150138c1ea0b4d904503639903842cadaf046f8f052c104e019b7556655665762586878606556d561bacca1298558d394881643e7808d54ca00d4cab025adda030e309038d6ee41933810e4073464c17233b72f0e179f6a7ef66c478d1c2ae6e2eeeed848935783dbb60a1c259852781549c345902f1c10205b130ac2c7efa80f9b24206b146e6b540c1d51d3970ca8401d3858b962c57eace30a809ab04403797ca14a8f905a74d7486f796932545fb03072dac304d98bc0912211a20cc5aec827529d0cac21a8be0a64d0c437cf0dc41a04e9b25499018f9b50ab8136e24e43536cdc0b5d5804195004a8a0ce17ad4ce396346cc0e1676af0ba1ab4a50a04f761f3cb809b0aed2088143c016009e1cb98145000196a880fbc001c302b3545504bf51559dce4dbf4b55967e90ac1e998a7e877e86122cb0a0d04fd00fd04fce2f0df0cc2faf8a8669664ac822b31a6310f603fb79995d630facb87e5b66b0aac14f8195d62f4156d60f0bcbea5765024df3aa6b75313f4070a510595426afc6fa800af64173d6c8e56d9f69e79d0c773dd154187745e35a8e2c0ef98ab5570e95393e3cde4afba783cf1f7f2f501b5984f3701bd28a2651880ae7b078aef4493918500cf3f9e19481c2b097731c054a75e271d7b18e58fd87aaaa5779303b5616b3b6da32b1ba03d3eacb6c6066a9556f0742a92ac3fa65655d01ad3ad84fd87ffda7c05c5105c2ec581cac3a10b8a03bb832bb0906b4b2627f0a7edd344de0cf846962993fd3045ed9f8993f13ebf725679a572656ff990c84c0dfd5d599d66956fd7e554cccaa2a2d33d4cf4cd0adcc9ff5334d93c6cf347f5975bf083ff37755f53333fdccdf0a13cc3481bfacba5f03d30456014df3679a573f93cd0a88d5c02b0b685e317fe655d50f38347f5559a69980fd2d303f64995666400f26d6ef579502b57e3f338303d7bc51952a2051a5e2d7ad4454f59f17b30a785665f53381dd2a8a19c9ecbf1e0c06bbd9cd2af3676e32b37e1a4cf367f5fb014d7335d5cc2c73c1eff7fb5db19966e6eff7abfa992810cd341584655d30197089fda49856bf30b3ca049afd17f73bb5ccaafe2b5ef5f3ce4c02ec1310307b083c3fa079655661990c4ce094df2f0bb632e1ab5fd5f95b90e002b04a0bad4a25a0e64feb2aebb766fe7e8b805766825f1550cfa4f05b9a2630cd4ca0e067666199bf10bfb21f1970acaa03bbe9c0bcfafd4c06554160d52fecf7fb995766956d263033155805b04abfeaccdaff136577b57d0bbf6bbbfe9a66eb8f27d7b0fbfa5aa5bf9f3f1dfb6fcbb3faaa2ffae7d75e637a55ff976defe19a76e355fdb5bc7e77a5d9d74ee7d8bf755dd3f2faae71eb536faf6accbe703dcbe9eb5f7b6d53dfd2e8fbb6b597d9788e679f7af4a7ed57d9fa4e76513475e137456dfb6dd3991eca735dc3ec1cc3afbbdae967e919b2fb5798b6693b6ddf7576575a865cb3f4cb7a34b56d1fcfb64ac790633745ed798ed9f6dbee9bd230645bb553155dd3f9b32d1bdbf30b79453dacd6f54aabb51bbbf5ec42a66738f6b5eab26bdcce2d3db7903d5ab32abcc66f4cff5ffb99858cb2eef7ffa7efeaaa2edbce76b23dc3b06bbf9e4d5d0fd7ec5c27a3a97fe9954d6b77765f569de9e4989e55b54d55fbbbaaedd67e4eae539a76e754f5f6eaaaacebe564745ddd38fd73fcd9d4a559bf42a6ebdfd6e85cbb6bcbb63ff57172bdd22b8dda6f6ad36f6bd7b10af95d5b1a46dbdfd66c1cbf750c27ab68bdb2bf8655f6a7360dc729e499a5fddcb6746db7aeedbaf19b0cabe95cc7ae3bcf6c9cd66d8c424ed5d4ae55d5aed3754d3f1b9f90d518ae611bfdb11abfb6cbc66e321ab3eccbda2c4dd3739caeb189d4bba8fbd357ad6d182e21a3703cc3ff75e99886d5f68549c83f46ed1a9ebfedff9aa22b2ccbabcbcea8676bd5d7703aa3709bbcae1fa6674fa3ea4761db7ed964d745d5d99e57946dd314f6ef085975e11476d7da7dd3b7a5db5784fc6d7a9e5915763f4cbbb3ed8690db184dd9397ddb9a56eddabb2064b8feee8cd2bea55f745d61f7831cbf9f5d63dbff5faf30ddba1ee4b9fdeaaaa66c1aafec1aa36e079956df945551b5667fbbbabfe520b7286dff9fda1e46614fd7ed9abcda35aafaf5fb786e7deadb584e6958fd2fbab628eabeecdbc2f24bb3edfbaeb46bb7a9edd9f69567fbcff5ea7d8baeadcab2ad2bc329daa2a9afe1b8467f8ab6ad3cd3353cc773cdbe350ca768cbca6dfa7ab5b6e1cfce2afc6b76955bffd1b57e57dfaef5ff33abca36abbadff628ecb2b65f5b369559f7b3afdabaae1dbf6fcab2a8fc613a8d6bf8a7e9fcceaecaaac934eda229ccb66f0ddbf69bb242398dd93a76ed3676bdaa7e944d93e796a667f84d51afd229fc5734195e511786bf4dfb759e55bf6e9061177ee33a5ee7b89dd1d65d35c8efbcd6f4cbfad77d67d7af6b06798ddf9fbe1e66d199a53fac9ec96e8bd63f8dd7b9b667d776550c72eaaaaeddaaed77ddf97d5df582fcc2b64bb3e85cb7aa05798d6b98addfefc2ecfadbda4ca65f3a5dd3f4f5e87fd3b7826cdb2c8db6f487db36765d9a82fcae9eb6d9d785d7f8fbfa9d20b72f3abfad8baab6f72d8aa6a7dca6f1db7ed6566bcf7ed79620cf709a7ad47d6db4a5e3992e936738fe9fa6d9b6addfd87b32d9b56bd656e7d6db2cfbd5784cfef51bdb360bd3aff77e7e23c835fcb236fd5777fe360abb28f14d5f33ba126624cce8deccf0376d87680af762d8261636068175c9c0cabebc72be7f763d08e352fa1cc7b0858705d55bcc818606650cd2e1c70b5b90e2630ad2e2f178d3675a2a85045958576456e9f013c7aa3400d59f59a12f30a385259379c4fd9e104258ff478facd2ca2247414fe00923119d8867ff9a9e61bc70d182855d1ddd9cdc8a381570297a37519827dc20b529304729f87951b957fbaf9c5977a9ea0dd78649dcc49e89e296891619ceb513ed5c3b2b41a9c34bfadbe1c5a408bc98f4784863f25824812989d3eddd6c789f11a9e2d7770a8fd7b43c27cb464fdf6f48b7b9bebc685777c3296b3f0279176b3f824d0e27e6cae208fbbe9571b51ff18d3e9ffbca65be2461347ab6bebc3824e2de8489a377f346efa68dbede4dbedecd3dbd2babfc469e5ef9bd526edbb66ddb9665599665599665d7755dd7755dd75555555555555555d3344dd3344dd3145551155551155551155551155551358ee3388ee3388e61188661188661f87ddff77ddff77d5dd7755dd7755db76ddbb66ddbb66d59966559966559765dd7755dd7755d55555555555555354dd3344dd31445511445511445d3388ee3388ee33886611886611886e1f77ddff77ddff7755dd7755dd775ddb66ddbb66ddbb665599665599665d9755dd7755dd77555555555555555d5344dd3344dd3144d63f8755b7655d3a35b77b1b526f789b90aa3ff73ca3819f20bb7b9619bdca6820d4e1ecaa990939387f25b5d9b8a14c316225278bca6bf12af7feef17cf25626dff43e1ef2367ec0f37432f2e7dd74f0f2c3f1e76e1de86f87e3ddc6daed238b7848996d0bae90e1ac9bfdfc4a7fd7bb3934678479cffae6218efb24aca67ddadb39d2238bc7a292c68bf511f77be76da2775346efddf4b96fc38779f78713ad83cf7d6d8722e953fcafa41be186e149dce7bead4df66eaefd650d9f5461b28d608542e116bbb5723241981f4c0f260773ad76fbc93ebabe8f821e267175dd5a356cad67198442e1d9d97bafebfd37d765b9aead3452ef66bb856a52811af3e20cabe91077d5b4b4e4965aa4deebeacf5724315c5bab3f5fd13e95b76d0e9338dbbbb9aa3b20f5f91424939e2c3bc8dc90d0814cbeeed0d34349810a4c32ec18b1b602e923119c1d0c34449854b584898e437492c6022915e9d82246c589103022e938364221a63f01ea78277520d297567400c74ec55444a1c9f1078a9e181990bce598b27785668207ad1cb493bc03c4a470c8a11647c666e269140e520cecdee0a0f2e0b8a335060f021a601c4cb28ae401a28b00870b302348401802010eb3207f04b07135bcd1294f419a52f0db0d386d8e7e6c4842e2461060a55f110f836ec401cad4ae49597443ac923c41948b19362805a98a805662878d34af8ba382f1dbb871c4c81d5ce6b6412201071e692c90d8d8ca62ca364226d9c788cda3b5672345df1726583838d1e3a14fc9e04d2d4c96b4af43a4073bf2d5471f5684117d624060f20dc2d1264fccc0e3bbf284881a31a8173ebc01d632d07d7ac3a73aa06742d00ea93d546e21584b02c4d8d3b34605014d3ad0ec3d99bbaccc512fec0d817469a57e24f7ce00e91d51b3f242ef53a20d5b90e4f0d2a353c7110d1b3e8e5e113c4a9bf4c0a9e9d1cd95b5c8f20189de39424799375486969880a7227529e069b906102ab8bdac03963cbe7c054e8e78cb135c0a398a3ba360e9a5a2d10e9c4e871cd28234aa9d54743ea6440f4c82a608902f07fda9cb6d40c8452c6b3482404f57ae80d318002cbe08c10db59034a64fae0ad7f2a80d853c7920dc18c09c2a255d0fdc8d840682a1e707ffc125c1710c1528fd9cb8912982748c520a724f1128a08a28673a585ad82028a44c43a5ac82c5d35c0d51a8ec7c8525615403019565c0c621493594b48173aa898bbc1a2d9ca8a522baec6a540863a14e6fb495e484df5558280d240bc870c10b013174492067a6362b44d89271c93004e4f0d01363c73dc90a007e8e5f7d6e3439e41a665c1e6e1b73169bab1f506e39c29a863c2b7a773012944d7272a02117554dc811817784f462c59294523d76206b9c9073640a8f0a928398b2354db9047935218d1d2376161c05258f56e8bee2389154e9680f3066cf0815998eea04bbb52a7805e3a95f0526a3debcc63c788c4e98c00900ce23c828638d0c04942cf061c480800e57818736efd38fcf8cbbd69067401e91ba1da99a17276f6e6f0aa8333b348fc6a54c261c2489160d359cd8b4b74b168d1d944e132eae701a6e34ba8a6646e8b15b507c44778498a5cd5051cce988819f354fc5dc12844387e617c5106f1e91210377a6a85507cf46991369880be0052935678117442d12049851b59920e611db4c59680a4039443489106291b5f700af4f83324f60cc32e101508380179ac88ae079504ace543851080f0d4e0e4a566d426c084a49c144e30a6cdb991d6d8a8386949d8446aa49c31e4b6c1644fab421e0cf029b022143a39628f0f0374930e0850e3effdf847daf94d4a5ffa3049020a83c9a3e1c2242707478d787506689886d095b2d698ea847ee02b73e03800b5bce23bbba4fee9243d5f0d56d7510ad15958188854ef4c8aec58810a8e82e6af84854c153e9e49227796782d4a1d776c8c994b600a2add4302fa64b5ee64073150a65a928f542f35f43890a903ec09cfa527a088952c32ef078d6d8d096c430e49be869e780c8c1e76380f217ec14b2709b42e0ee81560f880b29bfbb397743e0c8088760487327e7c5ee0816395077640bb090f9b8716068a991a3bc04434f4335609438ada9c130d481840b041999944313491f7d93e4c018ce7064a2eecb128b1d3306b812c35700579f19b9555ddadd22433342cc4890e57815c973c928c48305cc466c29238b4d264d86993b326878786422caa4a30c96858cce27390805de174301476298b8e460c488b2c3441c118b70c4f06a96287fdcd0181b52c0800018e3529bd3038c3a564eccdda92405069234b60238682181610783c20b4d8a662b186902bc70e315e1c561a6e3850ff5458a17605b34dde9ca02c2452417665cf0b960abe3a241db02510b2e2dee16715e8baa146bf0550627168190b0a0f28845cd83056a17815d0227d01278a0a0b26b620783cbce421f541e5a440dea3c25f9d41b1e80a88b5327448e13a33b085bea562cfaf4a586c4a53aba4a8c202049818a4147805b190b5010c8419783ae04f1089bf8cd021d179c0432f224cbe61a41b09c11a9a1e7c0a898c317cc7d3083c4cc03becc5c96991c2132392c72453956aeafd8636305143d2bee8e2b34c030db58d5dd880b1577409c91615c5ddc042a2a32a968f3a5a2878a37157091e0c82c80dbf30037a2866390e2508a2d299029e0502980cae210db37790344b6f492b412e16dca1bfda68682148a3b0f50301900c50b1128ccfe9ce87402ce89202756446890808e3a80136e9470b73431dc70b891707b33c1a8cfc4176f4269c984072f26b04a0c2a214009a2a4122b095424f490780246624822032619947c805ce427d3b5f2794403dad9af491ba636366d3cda50b4b141621bc3a6c726826dc1084323b68c388a41d9d27b1ff19ad0bbd47b1ff1cb323c7bef22fcf8f922a41c022282e60a244208844788513442204024c49210215c843003d1a9c7582aed25329f97bd537fd403f5033a9a9e8549e7d193fdaec7f5101ded670ff6de1df4069d415fd01574ad043dab63f5ab6ed5ab3ab0ffbad9fb8ff84bf0039aa9cc08cc32ddf4d2cd2c5b1298f8affc39f829f8fd4a99a2b6ca18755c29341a97c8b23b656b7aef775b65507a99de5619b1affd645f5ed6b655b6a696a1f5de339c6b6868675b6509ca3af2b834def4797e6471497f25ee8fc79b2e7a65eef16ea572897fe46dcc60238b696a1b4ee57dc279b833bff1cece9f179145fbc47f7e2f6b9cbeefe7652091a2e832461a8aa2e832c6d969bc7fd2d3f8bfa3904306daddda1dfd69ccf789c4959e840a39cb011990df38dcfbc8b3a1d213e9215e14dac6ffe04a82d8015b6220cc14eddd54ebdd149a695c5b61343aae14de451b594cb3914578890a4f34e4ddaffa5658bfcbcab4056147fff59baabf56fee312f993fef8e99feccbf73eb2885c6e818dea5b608cfaddb90526e7eecc91c53b4beffdee547ebc9f238b5b604bb6c084dcddf9de4d0ca685b9144cb6be40295f6c7d2189b94faaf5e5b8dc79c4fbf849cc7dd2245b5bebdd1cf3aab7baec5450bec999d0adf46c752dd87ac0b7c505616beb4717a210d668c16962c321c7072945de75f9117c800007a59d157a99c603548b14714445120448e0c79c3c56444cfc993be0861a451084a110e4a6c6aa239602bd15800c374675750033234927a01163820d31f826a84510448350fae0c20a524e210a68eec490e809b3d441cba91869392270a02a85cd045288ae37a7e06ab739a866166e1739063d7af0c92944716ba87e04be513ff24301d86242e64f6a9a48c9da00ea804174483478f08255648891f311b29381d7404c823875901422072431b91b606b929c728296b94d840c30be604b66831f5798e25a40552cbce09c698b400eef0320380e3e03844b666bb610488f084d2a88bb28a126a59ad12942a00e3d4214fb1a48d539712ac4878fd8570814cf869367d30fc00512311db6dcd8eaf32948263d583c618204c903dd29b83d633724742093af63638066a938c249f9971e4a0a5460722649ce0fcf54143a641ab8d656207d2482a3624f1521b49854278890698830a96a09931d61461db262e0b73100a3933416489928159c3b325569305939c42546c58910302051f1953a01eb10c9d147d6088598fee4b704a8200c8b15065194804dea40a42fab78b19205e90dc72131881e8c9d8aa9883ae3060d961817295c9c1e9441d1132303923717615d4419cd3241900061ef0acd040f091e418a4ba013199147610f9ce41d20268583580a45efa713842ad9901047c666e2699486af0c9fca18907048431803bb3738283c44b6e29c51d2b1c3115d97d6183c0868808d8a4c9d6ea6d86f6cc1ac227980e848753a838aa1f5654aba61604690803004821494c210b011634ca5395d41fe0860e36a461737250045f9e2944981609e8234a560146d485d8110c34787d302626d8e7e6c48227b5292fc8d92a4aa44c10b58e957c48320250a92949d38942a135c1050a6764d8a2244093ca459a08f4a11056295e409a25cccb8b054f3c1e92c54182eaf205511d04ae808a2010991ce9ca802c26cbd2e8e0ac6ffd67938a93820e5e7221c31720797411ab0490f7b16b6e427e12b01071e692c8ef079204660ae84972a6f5b594cd946c8dc8660f8a20323101c1ec6d83c5a7b36de8c6c00d4604717569af08b09160e4ef478e06007e2439fa0d51c03b818bca985c9829a6a4bf5930b04d553c6223dd891af3d9e6899b2e50d920aac17601146f489018139c25219382e969446135c38dae4891978c4025828c70a38b3cb42f684881a31a8971c069db108ea28325bc10658cb40f7c90d13b78f464f95ae0610e07240cf84a01d5221d6dc1c90054730ceafcc2d046b490018544421c1c343b3d4e40a84352a0868d271c6cc904b104d1534e53a1ecc5d56e6a8172c84a910c0ee470526b504205d5aa91f490e300800a093a0413cb44000a47744cd8a8b3d5f677c0780591480d883441bb620c9d19585012179ba1037135e12ea38a261c3c309e054971a012497212884e151daa4074e18a9fea1c4ebc29d15ae6baeac45960f472ec5153fecc9b94b6a0447e828f386cad01c3ee4a3894800841231e0a9485d0a740a12c40bb28c9bf5d174014205b79775000d412b2cb098712cbb10fcf2153839da71148159c195a6d3a44c625c0a398a3ba38547831738202a2c1522e3a5a2d10e9c0e074171dc2881b88446765b0bd2a87652d10dd412000568c95b1aae08257a60123445801c611de05022a00bee87d7a03f75b90d0875171a0588123d2feed6b2462308f43cf8c1f662cb4534035393089cc60060f12588008332b0314b528bf1f1d542d2983eb9aa1d0d86dc21f38089c26feb511b0a79f240df4b5c44838aee923916c09c2a251d8f2d5a422d2d9da2fb52fc8a840682a1e707ce92a826426ab637745c7049701c432d2f428660046863e5891ed9891b992248c56c495259909aa5bd990386dc53040a28223d3e95e1f2e2070b3ca8b8e8606961838050950c2cd844fa91c19292a15256c1e249aec9223a458a571ea16a0386cace575892456022a5d2320185d89ff1406519b071289226365452ace9e04546a9ac0d9c534d5ce475d5d9f172006d13001c2038514b4574d50dc000cb000b1616da4c3d0863a14e6fb4459706014a5c64c802ed9180df5558289786f8209445f6486d8d5e58860b5e088899c35285d1958eb70720b8d375666ab3426444a52f3701f824ed0c8141b1ba40396240657c6b94ee24c4376018027278e889a143ca853b5bdebc703282baac00e0e7f8c5878c04209b0462ec740213cc21d730e3ea508480a20c2d75c4038488cd596cae7e4071f063d19e1f0511ac965784350d7956f494802461b197d80c5a8291a06c929363c392056cb0c810c264025c84aa093922f08e50942082ca94168fd6cdcb8a2529a57aecb88dc7297bb6740d5ab64ec839328547c5422716659011d6ba1411c494ad694a255cc42c44724ba06c93a4af0969ec18b1a3808d19206cb002513a72696b8d9296c39ffbce912b32b0285ad101ce18dd571c27922a61c97aa2f461d2230204be0618b367848a4c7155963d438acc2845ae09766b55700a50806a9e709930dbe8c87e15988c7af3cec51256b44954258b1030b68110784825c0288d003009d88854691017133801c07904e1f004a3001a7ad8045992ad91818092053ebc0f34180c30e0c4611318023a5c051e5ab13f452441506061cf91073f3e33ee5ac30d44481524ee15c80465341838b5e34accf000343a0e41f0e1160642dd8e54cd8bb3c38f8b545c36cc42421eec4d017566a746a546579f067a4cbd41122cd411f2a5d3dbd99a4d0f4e24aae2290e4226130e9224011c0d2d1cdc5d38ea3078c0894d7bbb60e999f068418b4668004909944e132eae948900f579a444b846035c1890304236f296747df1c0a386011010f9d0553433428fdd82201054fd45aa0d59d420dd1162963643f89529dd441f0a9594889d8e18f859ebf4c6d4055bd1a3f7d0892e4b100e1d9a5fa401104482170f34321c826f1e91210367c60803812889e9d3a9c81eb13a7836ca9c40c3400ad7a1ed86985e032fbc20a5e62cf002c58b2ea78051ccdc41b425418019559b890e267e9cb2b24707f828c257e03db5c6202d0421b53b3a53ae56af4c59680a4039c402cf810b31f55115045b4408b1c8dae32223a72c636649d89526630dca3c8131c35426ce0d8e133a96cc3ebe6a10f042131911235f48a11ca628d9b45ba0949ca97082d016096907a8953963226a0c4e0e4a566d4102a591fc8479315843e7ab94144c34aebc89eaf8f050464c0d199f6b76b4290e1a52374c169519911769cac868a49a34ecb164433b01802c8a34ac390546a44f1b02fe2ca0c0c4d2183590e220708a8b90a1514b1474b8846b2a12630d006e5d26c180173af83caea94c182874158cac4108fb5e29294b4b3a1971804dcf47a1520f4a000982caa309bfb080112b330005f79421220447873fc854a34ecc130526590a03a1cc1211db12360a75b6ead8b2dc4880f3608ea847ee82960d9e88addf71f8064502002e6c398fea3829a80247c3c4141b43c2e42e3954cdde03f7991a9bb2f495c2d2f5b2e59fece0818104090c0b298473a5205a2b2a035180335cb8e0203bd4424559d823bb1623429ca234080b9aa92307e890d2a5868f4415bc1448981d2cd0db8fca155cf224ef4c903aae032a990b740ec11821b6434ea6b4e58f0126563ca8a13eb0e18a971ae6c574c9cbbf0233e280504a8a9c08869a1b723a81c40d92400c6086022d59112894a5a2d409adf9f1730d285795105e6b285101d20790d1624280eed3082f3f5b3280d808d487e4b81307d05e06738c56043d8444a96115983a9242e16951cbc0ded71a1bda921886e82369866078dd49436a4b4f3b07440e3e155b8e6c45da0ba4e46e0c2f22e453ea130157c6a80844670d98f965a79085db949d502738e10cec0c644cae560f880b293f190b2089a1f566821d1e1905166b679eaecc9c4890127340cb4201181c19e1100c69223c8045486195e1a8039717bb2358e4407dc120e008dc121d6f28d9022c643e6e347572e2463eb103edb304d6c8515e8261a70396431a5a52b92825e10118254e6b6a3074199436594a7543edad0c0917083232d50840cd108a057c46a1280321e9a36f929c1790e830b025c9252d018b07495a611c0067c45251823bc08791a3230926eabe2cb1d0d1840a7212decb162955d89518be02b8f88029f3409c9c132b6ca67459d5a5dd2d3224c10018f872c497a832c1aa36b31438c6bc4d71cba0a18aecc4535896e35524cfd57561d13238ea5ed1a38b070b988d58ce2853a43b0c9e2011c1066d326932cc202089d3252b8706f0d0458979786422ca2423964a212278208caf62d6852ac5150371769556f889a2c401b1483f827c928350e07c6f44d46853b230c1c615732486894beec50144366b7147c010b1f3da61228e884537fc9c4c4ae15b341db9b6d42c51feb8a91d4a11624f0615158c2c23006129480af376c4cc9f0661746061dbf4058c71a9cde9a1c311440a0a40450990a72d2b27e6ee5492427379d0010a9f4c0bea8ca5b115c041ebc8200182a4c171c4800d405f46ac4051e3c0a501a597851e351066105c5939d85a98f2a18c23f5e02a5499087f4cd16c0523edca8f3575de28a060c6490730af082f0e331d3f51e65dd6a5236150ae3d13a450b2b4ca283f958e420c9d17c88e800915a647c510140e2e916ad8513904b645d39dae2c207640a0c0181b0c7c371e705d9a531413d6c71e667d8992c3a38751990835077a4bca24a72cf074c44d97fc0904d68297514a4f171e69bd3084f683c9f2410c4c99d2ce0ec8540979666b8ecb604359a92d3e132258325c26a15011a5828936087a14e1c9c26aa8d026ae0308313eb2bc0f084582796488a3228105a652c5a9a26628a18022a83e228137e40de11f45aa0c55989fc0a4b734b81c8962809b08ee284de94ab1065f656c6aa0c31e1f3cde52d983e0870ca1eca439e2c01e52b7204b9a2038083be580d3954b8e2231b2f3f66301994f6f5d0ae017c01cd871094d9524d150273b5c825a42bd796066cf1d80c385037c0754692366458e8b376b1ee4fc3c41c04b492755c1eb045a020f94538b0e0d406440c11a818fadac0b580605f97c919870a6d9b2a4549508213cf4f901b735c783e9a781aa30274ebcfaa0f2d0226a809eaaa2e00cd4b51cb195e4536f78f0218994331dc4420465fc8960690c21be32057058a08406419f4aa6727cc971627407418ba94c64739c78080280025b8b3e7da92161e914914952c2a4ef9805c69e9419795303571371a483f778800c912f461090a440c5b0e0c7d387c054d2e9c8e556c60214047064528001c89d3f0a3e19211b6166cb102cc23606bc68599344426e0f8c78844dfc66014869450a3d6c042d72c20527818c3c49322b6ef20f6b1b1ab88d20836039235233436951c0a90c0ce41801041b25e3529918708d1ee439150742cd027a9006e6008b1c0a26392094ce7d0d2090a8cc0c12330f783259c2565cbab28491710984235ce89cc1555053228703f6c5ad93f2c196067e0188c6961865783241b03e299190e72041061779503c8a52ecca5af066521805e5118e9938018fbd216c31973c645d11941820f49467cd0f9ab1cdd3069e14fc52c0a7451da39a56914e1bae36dcb50993e3684c9db66f52a30178b4191ae3a7539794303b56431e4c2736f597ab82020c413e485b08d419e3dc667d462d52d14182180cb38d559d8d0474682901ca09843bd5cb2b8051556df33ba5c7950c1165c2ea0419f07ae25e89302cc35a81e1cd31c38a99e2b4a253a919a96e08411e49a496e3f49262611106b51dea1eb8c5fde80105786cc96e2205a13325d4114f990b299664d911b6d009b4402a1814dc3c40321449077ee82b421f305b54bcd860d56bc65cfa19fab6ce18d2d4a06b4b06961f08c82900d31028636f471eb815c0669e3268509e258dbcc0f9e2b26559084a930865964b230ee6460fd7580f518dd8c0b8b168a261ce99233cf158044524516205619044d88bd2403564277911bf93bf4e97c006e4f9b75ea53dddb2ab8daaf17c49666b54f574eded97653f9dc296e4375dd5b6a5ed564de3f94dd34a32ebc62f4afbb9756dd5ae614af26cbf6cabc628eaebd59d5794f8a6c33e65fea6499fa21eb2ea0f0d7092fcc2734ba7ab0bc3331bbb764d7c53e40dab299b192a7f6372496716f766f60528498ed1f7ff768d59964de3f8d72930120cafa9eaae28bba628daaa6decbe705bc3ac8ac2b5da022145003e0a033a62a011a345404589862710d1a12104a0a1058613808586138084861b808304e12730500420a0e118f08f9f31601f3e5e4005f62c00ea916d38455fba9d69f7756796ade301807964b44d5197a6e37665dbb5855b3cf2afd7d955df1865d1341d2620bfab0baf73fcdaa99dc669bade91d93985dbf865fffff5ebff8ca64304e4794e6dda8d5bdb7d617b65fd9ada91bda7eb0fd7b0dac26cebe1e11a601df9fd281bb3764a7f7b4ee3b97edf000f9051f58ddd1a46eb597dd1394d492055a8b10af762f8e303a423bf711bab6f0cc7f6bbae369b1a20b7685ab3f36aab31ccaa3fade7c86bccb6f63ac7a8edaef367e161edc200ca9161f765fff72eabce6a3ddb9ff8a6ac0b601cd986fd1bafe9a75f3a85e1d755e1c870cb7e7ba5ed368ed318b655780ab00019fe701da3ab3bc3f00aa76dfabe91fdfbc6f3fa7adaff799dbd97037423fbfa9d6975f568bd7a7b9ded763d00d846aee714a66d549de75fcf2afd6b201b99ae51156ed9f77dd1cfb6e94a7cd3364e28dc025c8303a84666d9f9f5f38baa745c7f7a6e8104c436ddc26cfbcef447d3fa7bb817c31b9846b667fac7adcaaedfc3291dd76674255ab4702f8643201af9bfef57d7d7add51a6d6736fd368ab8dace009e91d3379ddd959debd5b55d3465ef7b3623d7e95bcf6eec7f4d7bf745816564b77e5d357553b55653cf7a380e908c4cfbd8a6e93665e9f6afafafdd00c7c8690cafbeb5d73465ff8aa24331b2aaaa74bbbe6eeaa2294cab339edfb46a0307906336ae5b974eebfabbb5eda66d1a46b659da55e3f97de1556d53b7351a3dadaa4e8060e4145dfddbdaab4da3ea5cab2cf14ddf24c02fb25da3ea6cbf2b8ada7ea5e799f8a6b78d2c7a595b15756d6fd72aedbfffc437ddf3ca16d8458e63bb653f5bd7f05f5f7856896f8aa66674255c571502e422d3ffade75fb7349aa62c5a7ff514708b5cc768dcfa9a46dd8fdab357e78442a1d04ded00b5c87efd7f8d3feb5df6c76fbac2648059e4f5db76fddaeb3acf76fc69144dc700b1c8705bafdf4ee774a5e71ab55da000f25bfbd94ed114a661d8b3714d7cd3373ec02b72bace71edaa280caf2babda30f14d5d2b358055e4d45d69157ee715666bd5865fb74de700039069186de3d45e6196ae69d5a53d314d3f8df4398e746bd3392015b565dd988563b56d635655df74756d155deb975dd7afd629fd02a7c8b4fbd670dda269dcaa29dbd2c4373dceb108b500a014398e5bfadbb047bfedfe755e896f3a2cd6b57fe51a0c1000d9c369bac6eecba6ef1aab2e3d4cdbd70c308a0cc7ebffad6fd3d666d7f7fd693a4c5b150f108a3cabea1bff16565196565bba1da668aa07f8444ed38fa6ac3aab36ece779b65b01804ee4d6f5718daa2e5c7b754dff8f87e9156013198e5d1446d5396659d7f6edec95399f6cc66666c666c66626824dd50099c8b1caa675fa61b4b5db5fd7f312397dd3348d5d167d7fea7ed62b91e7effad45dddd6a6ed74052671eb358eed76b6db3d201239555dd6d7ff7f3a56ebba858fc8a98f3fcab66c0dbfbe85632372eb6b775ee39a4ddf975e5b5706b088bcb671bdb23e4651b87561741ea6f9c7537a3aa1106ef179061291d139b567755dd53a66d737a6db1738449e69d4ae6b1875df977e53f613df94758142e476b669f4add9d86e5996ae7f10978ed11fa7df85dbb985896f4abba90b448ed797ad7f8bd268fbaef00aaf69db027fb436fdd2315abbed3adbb0ad07f491da710dffd8a3ac676b4fa451b8857b31e403f6a8d317fe289ad6efecb6765bb7702f8679401e724da32cccbe288dc2f18bda682a198ddbd4655fda453fec63d54525ff75a6dd1a56eb5fc7b48fdb53f247519476dfff6b777dd79826be691bff4cad331cafeffafa148de73836fe4ac9f6ec57d4bb713ac37e4557b88d405d591af628fad634fbbe69aab2ca3cf97fa4643aa6ed36a669f6b57d6ac7ea30fe7951c9a2cd8ff68afe46c92c9cd2a84bd33f8efd3b7f1ac44f94ecceeebcbe2e9aaeb4ebd2e94b7cd314c50f02b94e5d5a655f75ae53b445d154554ffc42c9e84fe39fce2e9bd26c3daf2cabf6f981925fdb665d3ab5e1f7d3be46eb4ff21bcfaa8bce6a9cda30ada2fe01778e5b14ad59746e634fb27fd9b57dd5768de17686d3b7938ca2ac5fdd96f52b6cc35ecd8b7b6cdf47f1a6b1891fa7b44655f6abb10ad32a1bcfeea6f655617aa6dfd6bbef1aa3b003323cb7714aa36f6bd770eceb56d38ada36ddfedafb9f49465f8fc671fcae36fad3d4a7985e1475d1975557967dd3974d5d157d59ffe7d6f5290ba7713c13df1459f4c7dc2e2a6b366d4046e76fdb68cda2aff72e8cc2f99fcb3fae5e92e3b44d67f7853decdaac675118cfcf92ecc6f4bbc26d3cafaaddc6ae4d7cd3b492ecba73cbb66ffb7ed5d36a8b2c1259954d29c9e8ecda30edba36fddf188edb25bf49f27f59749ddff4b553da9dd35b59b3667425cc92b7b24d284cdeca5a28c493771cff24c9ef9cce6eaadaab8aaeecfacec4378de9dedc95cc2f926cdb76edda2baab2a8dac2e8170a93fef842925dba65ddf5b557d7bdf9f2a5bade7cf992bdf37b24a3715dd3b09ddab347e9d5fb21caaa0bfc1cc92f8aba31eba2313dbfe83ab70cc8351dbbf05fe3575d558faeaa5098e3d7484659387d63f49ddf7a8669d425be699abe702f86c71f23796e55dbf5b19fd3148661ffd5157de15e0c8bbf45725bd7efcccefe5d6794465154286c5db817c3193f45f20bb7edfad6283dabaf3b7f3a91ecb6315bd7b1cb7eefff7f6144b25ac334fbbe6becfd8cceef7b48feb10dcf71ea3dccfaf7c32df14d8974ee9534ee95b9f2c687e9eae2674856d7d4abb08da6343dc3754c2f20b774dbae6eebbe68ed69ef63e29b0e8dbe38c7297e8564b7fd2e0cd3b45fe7759d5f42b20da329faa2b09fdbd9a65995f8a6433657deb89ac68364b84ddb9a855bba46d339feb620d946dff9f5739cc2b61abbee4a7c871fbf6f22adfe02cd6d4caf2b8bdaec57553fdb80d4f7755d967d63d49d7dbdfa3faffdbe685aa7a8b75db875fdc431fb616fd7b3bab634fd95c315e98bf53fa6697eeb15936d1f1fb76b1bb7e867e1d68edd9ae8b2347e7ce4747553746559fb7d5b9af631bc648f4be49a1f6d183f05e4b6a57f6db3769cc2706ac3f01e196561d66659d77de197a65d578f8cfe76a55b146d591445e735cd23a3280bd72a0ba73e7ddb7ac623a3a94f3ffad675add676cca22a9b801cd371cac6738cd6e89afe96be23cf2eedd7f58ddb79fd6ffb6104e4f7fd1e756b54fd7f46d5d98eacd66efba6afabbe29fca22a5b47ae6db7aed53456d5559d557f805c7fd4af5e6dd3d68edbb5457f74e4d555573ba6df7475ed165ee1113f03e415a65f1a55e9f9ad57dbb77e8e3cabaffd3fdc7ad8bb6d5bbf257e7264949e59b8f5a9a7d9daaee91495336333435798b199a171666c66a80a33363314ce8ccd0c4d61c66686be99b199a128cce2deccd0d4983c1687ca8f4416dd39bf38b2a77fbbbeed3aff344de33875336373d596cd0f8e5ca3ee0bb330ea57b665e9944d53571df32b404e55d5a3f4b7611b46dbffaa683c4c8ffcdec8688bd22a4ad7bf7d67dbb3ae919f1b19ae6db8f56dacd630bdd629dd464ed7d8756b15b5e9d5665b356623cf6becd62afcfa5aadd9d9bb6b641fbfb55bbbb4adaeedcad22b1dfa53a3ba6d9cc66b1bc3eefaae6d9bae6f4ab7b6cdbe6eecedd9d36acc778f100aebcf77cfb831792cb6567e04c86c8cb66dadaaf3dbfab9fd699be49746f6704ca7683dffda5d55daa368e4d7657f9dc63f7de9344d57575d972c92df19d96e63b86dd11fabdfab2fcdc26aaa0ef999916d989d5bd4765d36557f5bff32b26f5598566737a56114fd9e7597b44f51213f32f25fdf7576698fa25ea561a3d10f5fd58cae84d9d075e15e0cf97e63e4784559d67de9b4b563b48dd5017e6264566d55f5dbae4aa77e56d394f8a603b0f88591d3b5555b986e51d7c7a97f57e29bb25c7e6052d7ae5d9bad5bb5fe2a3c7ce9fcbec828cbba2b9b7eb5b5db3fc335f14deb616dbce9218cbb877e5e64f4d3b64bbfeef7e9fb69d713dfb4edf2eb22bbe9fa5fd8ad55dac7b05f57e29bba5e7e5c64ffc6eb0ac76b0cbb2eebe996f8a6eb96df967646e3d94661d466d99a6d896f9a77dfc4e137d6c521f25655b011f3220ba35184199b99199b59cef773e4cd66c66666c6666646f76626b368daa6707e5a647aa6513fb7ff753febda3fcfe76c875f1699555f96a5dfafd6de45699bc622a768ece19af6f4ea63556d6901647466eb157d51b66657d8ade72bf2f7318dc29e465db68dd1da5b91d198b63f4abba8cda2ff8ddd2a72ebdaab1bdbf67767efd3d40d407ed1d6c3708cba714b45f6f44bc3af3b7f34856bcfb253e4996d67d655d5ef59dbf6f12a4586edffaea9da7a785e5777550190e997add516b6e1776dd3d6a351dcd74e5514467f3ac768fdda4221eac2bd182a7f5050375e5b585d51b845d91765d1559d63359eebd846573a8653146f39ff36a1303fda4261ecc2bd18def83d91d5b665e3946dd70fb77fb5692732fbff9ab6ef6fbd5fdbd4db4d64bb5e55167d69f6a72c4ad32a871f13b9fe6e1dbf765dafa8773d5a2f91d73fa31f76e3356ee94fc74a643ba6d734465337f5ac7f93c8decf9e465bdb5d5fd7466124f2a7d59876bdedaa696cd3297d44b65dd7bb336ab7b41aa3703d1b91d79fbaeadbd62c5aa7f4878bc8aceb6bb6a5d7d8dbf5675b4f44ae57faf52d4afb19fdf39cc243e49fc6f13bc37e4d6314adbf1039855775a6e7d4b7ee67bd7783c82acba22a9ab2705da7ed4a0391d9d47bdad3abed6314b6df1fb25dfbcfd6ef9b7e14fea9dbfa90d91746bd5f3ffb531b4ded1eb28f5b1b4d53ffce6ababaffc743fee8bbdaedeabe350dc370fbf1569883994af66eeabe709aa2306cc729cda292630fdbb4adda2cfdce2bcbaea7648fa66b5cdb689bae2e5a7b53b21acf1e7e3dcdd63e86ddb694fcd6313dbbf09cfe765555558d407ed1d98ddb757653f47567352525bbae575b1656d5597d594fcc8b7b6e6af46baac21c25bbeefb6b9766e9afb668ddaaaa6ac71425d3ae6ac7bff5ad7761b7aedb341dc2312190e754b563ba7ed1d8bf6a8caa396e4642ce4ca8a6a88a86926d56ad63d785e15fcf2e3d839251f6b3f64cbf6a0c7f36466be36d0ffb94b99b50981773b73b47ba0985ca9f1787778e2c0a85f4d743d34ff2b7e99a4657358569b4fe694b7c53e2b046fae1f0ae53f5158144baa1ea0bf301b9fd1efd2b6abfb00bfff6fd9ee4effe184de7f58ded574d7d27199deb3465e9174dd935fd71cd494edb744dfdfcd17686e7587d37c9b64da728fcce360baff3fb5b076477f6dfaedf15a65d5aad5356933cfb35b56b96fdebfcb6eacf24c733ead6693bb3b53aaf33bc629255987e63f4b55916456b5b551b903fbca26c0d7bb87de3194ed14beafaba70daa6a89ac2e91bb7ecbb7e369efd87df588d5fd84e6de29beedd4abc21112f228b766a49865fd7d774adfaf9fd1ea6eb4a72bcd2f6fb59757ddd79f5b5a7247f97f6744c7f177ddf5665db4972edba316bd7706dd7af67ed94f8a6b19b2b6f9c6d479892e43945d3985ddb4fcfa9fb5b37921cb32b5bb72c3aabe8eceb9f429269d766edb86dd9b66e5d1b7e24a7705bbf70bddaedeadf584d1dc92ebcb61ef5749ad66c1da728f14dd9b44f992365a40a3336b31a594461c666c6662614be874c06e478b561d746e7d5557fda7abb91ccaef04bb3acaba233aba628cd4886eb54b5d3164569f46ddd3f2f92d1155d537bfedfa369bac2ab22d98d533b4dbd6dfb165de13625bee94f24d7fe6d535a7d555bb5e3d89d11c9f1aaae6babbab08daede5e5b2214f390aca669fd5dfbbbdf9e53df7e43b2add21f9ebd3aafbf66dd0f2f20a32cbaae74dc7ed4b369bdb685e4d94e67efb6f3cca6ebb7d994906ca728eadf3a5edf7786e37590ecd6706aab3ff6a867e7174597c912744ba3170a85c2f72f8d5eaf55deb98fdf26a620f9a7b13ac769fa6b1576dd19790ff76e251e6c4657c24c84199b19ddb7dbf37271fa06926d9585693b8659374de759fd80e41586d53a6ddd0fb331ebe3d48ccdac7633a37b33130a85693a4c69bc78645ddc5d5339e61fb98de79fc2ae9bbab15ba3df8fec631a6d57bfb6b3dac2bfaefbc8f55abfe887d7948ed9da4d633e725ccfee5ad3ab8bd22ccdaaa902726dc7b45bbb30eae9fadb34bd474e5bb5566156b5d399f6b13aeb91e31486517b8569fba3f3eb7d1ed94ee31766dbda7d6bafa6738c477ed1daa7ae1da72c9ab6734b2397abbac44c4086fdf7e99ac2691da7ab6bd3cb3bb2faebb89ed7977657344d631501d99e59b88ed537a5e1d76651637299bf76e415a55df88dd5f6fdab4fe93a726ddb31cdbafffd9fb5d903e4b445dbd97ee1ba8d6b16855d3af2fbd2aa9bbebf55d7f7cfdf13dfb41d621a20bbb0aa7eb5f6ff5d51fa8d5be2fbce9163f4af28edb6f14f571faf33f14dfb7e9edaf7f3d6a44fd1c39423c7f33ccfafbb7a56b5d33aaee3c82bdbce5e6d633865514fb3351cb9757d1dc7f44ab3760d7b142e404661355d61dbaed5da7687b827ba69fd3f3e7c5333ba1242e1afdbf78dfc57bfbeaf0babedfc63d44d896f6afc6a2aa2c69f8b7fadb6702f86b0e946feb13dc32fdad2b05f5b7aae896f5ae3e36d647b5d51d8a6e33a4d7ffddb97f8a6497f1cd6c8ffca261bd9bfe84f6d9a75d538b659b425be29f236def4708dbcfa79addb955de7189dd59825be69cb66aa61d5747d5db44d537685613665dd9886d9344de3f9b5d935a5e94f7cd361deffc9b7681220bba9ffaafa67dfc6aefbaa7f55edcc34b2f7a9fdc2acea559a8edb14c643de4678858946fef4eb553a66df389d6dda5dcfc8f5bcba6b1dbbefbbba6c1a7be29b0e55b5ce3423bff3bcbe36cbbef53ac7b167cbc82ceab2b3edd3759d595a6d3df14df15ce993ac6bb3294c3232ebd6ea3aaf71fdc6b0fa6b9bf8a6467f2cd2bf1e23bfed3ab3b6adceb377d5b7fdc43755519862649785dfd445d515555f584561e29bdaf71f2aef01e4f9ad574fc3b38763d86df9fefa668691e3d4c7eedab2f19f5f766d67e29b0e919ec6c1c82becbe74ede9fa85bf1dbf34f14d871f57fa649aa6ea09f38b1cab703bafebfad26a0ccfff13dfb4c5953e4e5d33bdc8f1fcb66ecbceb37767db7d6de29b0ee1d48cae845cbe3c9a919033837b31348d985d64b8855fdb8563b8866178ae57e29ba2e9af55d5e422cf343bcfefecd539865b3aaeb7c8aa1a7bdb7b4fb375bcce68ac45867dadcef5ea55f4a3bffd6d16199e698ffa7a7e6d369dd3af12df94882f93c33ce3728ecde84a2855d41fb9a7027e53d784c0c4227fb89d57954d69d5b775ebb2c437b543e1a63a3105906dfa7f9fd6b58f53afceae4b7c533ce4d04d3f5e2ddbfebfb199d999b199a1306333cb97298442e5cf53faa3fde57e7dc3098542e16d349a4053f5d737926dd3b4857b31fc5931afc8b5ed7feb637855539a5567f6ad625a915bff53f6db1e9dfffba6ca1c1fe645bca8179bd195c88b78d1ae36fe2289e15028fc42619b50f86ba170d8c8ac227ff6c72b8afe744e5fbba567e29bc66b0072bcc6715aab6f1ca76ecd7e368da3985464dafeeb9cbaac8fd1948ddff88939456edbb48e5dbf7effd268abdac437550e87ab8abb299fec73df26144a915b1a9e511645ed9af6e90c7ba2195d09a110cfbb6f229a0220b72bccb6b41db3349aba33fa95b7521945765195aee1d9aff69cae768be6c8af27b3b012138afc7d5cfb9745dff57f9856e3f82d623e91ebb84d7dead3954dd13a4ef6e5f16d2385c2154e623a91d1387ee9d4b7ee3cb734ead2c437ed9b87aa6a23eb84c21a4605309bc86dfa53bfd6f3daa2693c7f96f8a6356c9c3d0cf762b86732916df7a7f4675dd57b1fa7314a7c53a150287ce349e612ef5597fe6d1cafb64da7e9fc954bfc36d243583dde4a9b557ea3d1b31d6f2509a130577e3de44d0b855f79d36daac75422bf6cccceac6fd1d4b56dd87be29ba242a1195b6a574ceeddc921cdbe41cc24b2fde9788e59366dd119b56b94f8a6a8badad51f0fcfe7c5a437de775241151389dcd26c0bab6b3cfb5fcfb11b13dfb44d8542612e140a8542a1599c9c9959dc9b99199b599f50684657c2accf2e2abf9d19dd9b99505827140a856c66eaba34a37b333321142a7fed3f5128a48b42d74e79db4323fdeba150981cc6ec9ac2bd186a79328fb4b01ad374fcbf3bfbd8a3c4375516d3f475611a715d947eeb8fbaf55acf291d13dff4c633fa211eb2882c1e87f8d055efcc2232fcb2f17feb36b5551fcf704c44b6ddf7f5766bc7e9acb6ac7787c8b00baf293cc3e9ecae2bdab2c4371db6a9195d09b3b93936331666746f66357229142a3f2e14c2702f86260f53884c7b16a6eb154555f88dd73526bea92bc50c12fbd49d593b86bfadae2aeb896f4af76636441687c95b59afad324fde2610d865d5ffa62ebca6ebbba22af14dcdea7769762c1271a19ed9fc2ecd3e8dbc51f6d348154812aed2fcc1a22c1bcfe81ac36afa57787984f3650a248916c693f7fa9537cdc2bd18c6993e64b7a5d336b65ff845ebfa7b96f8a6f9d15ecde84a98c942e1af85c25f2b3f8ed3a52cdc8b2109b3079baa28bbc6eafbbe2b9ac62a9a0a8f7a46a350b6611875bdafdfd9ab6a4cabc4377559983ce4b64655dba6d3dadbb00bab9ff8a6776e4657c2cc68f442a1f2b68542a1d00c559766c6e4b16846f7663674e3611aa7702f8666003b641885e7f4cfb61bc36afcbea80e996dbf9fd99a9edf95b557fbcd21fbf97b3fbf769ca2f4bbda2e0ed9665d0fbb9fb5d7b48d591a25be693dc44316f1e59aca6e891b324adbb5eab6a84ffdb7e9d5b621a7b04dc7b61ac36bddd27f9d6fe470d827c3718bd2e857edd7f52e8cda7c725cb3eaafd3368dd7b8b5fd9e6cb3693bcf6bdad22a1ac7ebd793dbffc66deadab63bdba9edd24b594d69377dbfbac674ec5918ce657a76551a866d0f7f169ed71797d195fd6d8daeffa5db3a8e95b28f5ff87b755ded577d5596ad21db29fae3d86d571585515b45896f8afcc621fdf58671da0efbf2a2d227611249d9b65d14b667d6bb69ea55dbbe65ba5e67d6db2eaac6b00b7f1b297f745deb949e6bf46dd9f6db4799fe6b3ac7b3cdd2f51aa7df36caa8ff6fdda2686bd3715d7b3b4ff668cbb6a89dbab49fd318b669c82bfce99af5b5abce6eedae765166e3188eed98ada8442e4ac6537114e4308a840c21ce4c4502005000e312403038281c8ec6c211d1903e6b3014000365aa6e8838170944812c87711404410cc3401001041043080106198f6cae04e77e4d80811c7d1fe40f7cc85ff6c3f37782dc0047833a10746645300de89382d487f9cb7ca4dc11c66d5529cc7ae340f2a5a12c6f2ad7ba45fd533110985591b8d18e8901b21d0d193d6eba46cd82c91a6d6aa245dc6b655bcae59e419ccc6d50602061af2f81bfb98fdcde2cdbab6b526899a39a5fe69516540e8def993f178482799cc71cec79deb7218fd60217d409440a16a555bd93ae9bd101fdc59fa2b70bf7a8e8edee04a317a3fb793bb247dd75bba3cd5f34c718f510686c8cd07c913056f476257307c2e866471ec37a4857f4b63f20a68a91eb10f30c8bde8e2feee80545317d4d3d8f1db97d742770151a972f511bea4576c5c804da9c93ab0ea3436dab8f2a42d49e74a451e04e9667c5456f072393de525e5e515df456b4201d26e4e8ca8bdeae38fb61f03b694e8716b930cd333ea2c801f9b778b42def75fe9b8de2fef8a643848c93d667ed9259b7a6b0d3de41a078692f6474b07263e44bea1576deb8ac62d49d9eeccaed6c11ecdee9e1be1b6200a5a722fb9798fe39ea75bdfa6fbb20bb16f68e8393d4828107efbbbaf4899368a22486f65991cffc8389618f6f0e4bad0b46895b19301314108ec7d59aff24001eab1bf80dc7121e43fe4365db026bd04e198fc576776a00ffed60af29eda7972bb02ff114e41d3c81219a83412f0e084dc3d119f00fd71736281b0fe393b8e89b7b5f41bf9332eed765a358c99387f8181ef7f5e0b25c92563a9f143f30385add0004ff4d226c168ef8e5fec8666fe6e330a47931ba227f1f863a8fd273caefd4eadab8d03bcc8cf3bc4f52981737621e9a07853f7ab6f01addbfa840e96fa2660af8f44921bcb8cea30023ca19a9308870abbb0462d3fa32efbcc54c14f5ab4a78f75d4a3fc2a64d4d5f27d0c71024f726557538e9fd339541c9c8cdac83dac1baf5676022f10ddf86eb514094942d20df63eb74b3f6ea209d970ad278c37a8258f19929c1131f55ea920df13aec87b57a543f1b8cefa5374441b5b082e45e26d7d31ff68efaecd9e7285552e01ca27f3008d702098dbcb03264abdf0cc23b289d0004ba9c1d02d628564e4218673525b5bd47107c3862e3ad3a815feb067b53f585f3aecfd843e629e19234051c450123da2122cbdf2039b0d36e87c5fd8538994465324ea84e28bcab96f140add54522b82b8c14172ce191d0895c5484981efe6f71298f75ba2634c88a5207ca22603c1ffbc34719ef2de1ead2477892773506fec083bd6c355cfcf25eb308075c235acc3a7ab1a0652459c03071d82e36c68be7bbe9bfa496b3725cc07cc26c0e7ba1f76d4cd14a046626fcb1f87f8bddc8fe2c94e80b1ebc8e57ab5caf6b2aac77f688e3edad8811bf2a81336a69b3a4edeacb21831e923ed035029879933df0d7ff8546ef40dec44f4fc6bc8e61705ccf91b12a9f65df69db4df9d6a3995b635452dc18f3119755bc74c12b011a724dc0b16155c5de714399959773473c7818ec730e991c800af0375f6b03861431e31bf1af1b1f3bfb4ba72369f4beb1a59bc2779aa13910d1c5cba09ae2205e685c2cfad7ea3bda14c14f60f7ded465e6f0cfbe5e2eb2f5cb094c62d55efe802751b4f7c8b0d359051498a4e091dea776e46fbf5c35d20da22bea0a6ccb4ee2d3dd1f7646975fec60d71957009ecb06ea4d4b504468024e2b9cb2bfac935de803b9c83f7dba59ff72b05c29184d71b86e08880dd4a04051287b66257931109b218c640f6a69f0d05009d46a261833a2a573c486f383d0912c3879e3c675c09dc8dbe11b102d3c4d2a17baae5b76f7817f749bb48c445b61971cecb21a065ab9debb7117c66f3248c533eac480c0f05f77b90de4b5ea9a6913b060a10f1071f46223ccb2567a453e79456c13deccdbbeef7079ca41f6d3440280bb2b73f1e813453236b584c068a656a127dd467400e2aa542ae2a0c0841351b3c77354d4552cb92b9a5a34c29bafd6b6e56af98f64044d18b57cce482f2eae55a589573b9730894ccc98d3af10d14c0610b4eecb21ceae77c48561c9218b523073bbec061dfd56ee448c065ced9f97a2ad53431b612e320965fe00511dbcdb51b6ca256cc91207d347467810cc2a9e447c42c4f41f80794194ced4d2ef92c3d9d58308c8268dc42d696fe091642a26574a54ed390495dbc46bf71d2be3b2b761743c5bf1d965c76ae6134bf5fef932bca600c24612919e020b450d4053860d8f0b37e9a235503786f43f64ac621ec483cec9722b638687699adf91cf75c201c6b62e78e66bcaf375f5424f6439d8ced43e220233797cd74e15bb3942834dfc62345f7a162f69856b938779c27064e48c1a93c916a8d1866e668345464cec00a16168841b51e49a57d5621b9a2ef47c6b35e750ef0462bea938e725e9271a7c980c8edf2f7180b4be2dd4a4c533405f8af226b63f08480e4effe818258cc2a87fd71fb9508e2914bd82a7ccb4839502668f302ddced92bee47ff65fc4cab327dd1d78ca0cac143037c032bbd4fc0d2e57258177ec93ffc89faf7eec0579d5f733f712f024d6659fe98e4c4100330806d8daedfba56fab0344b9ce344c4261e6cec911ad2729bfe6d4e28d2ad3ba8c2bf974184fa58ca1aa03152f41ab2c3e00bb24848bd5a949852e32a5c04a251fa7166e7a813bbffc9e6b210ab0f0345805d719502bbfc62bac014b81089cb6912bba6aa67e9f2e4d017754041de527ed94a9bf4196f0cd457c24c537fdc2cbeb00ed2af2109626e0ba95897df7b9c80e94e06b666df6a0922af82aaa49ee8af65cd673004dcb7db0f6479988a3b280988e7205a69b988797f45e1d076fe38c8ab64c39495eb211e1981d257a0d6a0a48a23891c649602927e8d0e26820adc5d3402a5b08bbeeb5b60939ade00b202a108d645fd354398f629d273636d8b146f514dd6cc2a7f9241330a3081289bec6a00741460a2cc1c69f0008ad6f1d6b3637e246d2445c2b2bb801ad21c732bc1d124ec549330197d6d4a9352a4332ce30053957b03643706d1638597d2c43602b361ef4f33627a235d547675b63907c5991a0f1c15ed886b4a69206c47be3f6ce5c2d2e0aaeef815219965aaf7bdd34e317d1542323fe2d93dba2b03b61d10b42a017e0158d1850ce8a40608c402f64a88a024ef70bfd11c1b278ff17158bf59356eb5e542c8e0697bd012bd3481562a30ce527b946908b0a42b78e00bd43e0482f30cc742ef02ff31fcc7cf6e3dca8fb01d77672fcd00f3b4b3a039f504f3aaa81b71115268f90983b09275c45f54251e8d1b172d8e5f157c0640d498f4f3e16b3d5e430650c24e41ff440864ed58c182afd59b0d1b736e8fe754f28881390067595981df2c08a1f02a1043aa615eda24c05f3132b67ff118b9145fb118ac0b963f1407dd0796d59cd5a157a59955a7c0dd393a58d47b1e37d353928a99cdd80d2dd197beb9c6b6377714a159e7853e28a59263c2c946c293e6c101c5ff5ded3ed149eab75c1145be8235de4fdeabb554a2d7bd1b3d21f556f2b5aa26edaa704746647d45e1f146e1f68126776e0e8a92f8171bbc2808b08605040533182f8eedddf3435c6e5070dbb24778dfcc80d3cc04eb982548340961c5a52981cd585e3542841fb13adda66a228284d45b996e329704aa269d44e08bce6435814bba7329c1f256ccbf5a8d3701247f4ca2e587ca1f2f677a14aba35515e5f3843b85b73d1d384a8a7ec1856f8c7bca1cb81e23610af12651e5c69bc3725ca3bb8d678af94287bf0d78da551badc8372b5e174c54a4b27bca4c4f563a93d5f21e4d5036869503d25c41db4ca06ab5525a87ecb19834d4bb0193663c271a301c686a109dcd7c56a8241e8593a200053edb8fdfdca02173fd1cc020cd76bf3dd5efb8bb0f616d1e8de2cf4feec1b5055c05d2c1b17fd013f43e87d1150f9f2df938cd4fc28d0a923c416c632e05154460200088324ecd5a44ed3aae40b5020edb0d681360682d15792758536276f27c6115428ce23d2c5f6e858231ec96608b549898bf23d0429615ca23e490049a50749ecc580286ba91e348e6130de8198231043ad02f9a0b322e7326a1cc2e14261bc5ed7e8157ad191e4ee38cad815ccbe16caeb3e53ab6c32dd5aae039841036dddb5f8b03ac44b09e88ada6f3b6bcea1c309b9235e1ed7a5c44403a67712e43031468a01bd6e50bd33d0bbe9564dcfb0218a505068b2f20e36bf8a7bfbb88c8500de14807b0300bd26a0dc9000e69e0682313031e34cad0233345bed64b97aae10dfec53aa5782839c74712c99540d40afeca464250e5e7378cfb430272bb65a0dab4378bc7310c9f68aa179c772a133449cac61e72f7ec13c2b6f4eabbdb0b061ede78fdb938b533f1f069f045c2d4fdb2bc65bd0edbcb3ae4ec60d32afb41cc92e93496e2d8d3c841beae78a4bd557066e9991f157f46d3b7140376ee23fe32672bbb272985b2e4eaef2e24a11cb6d1684acf5a823408e98c670e6c141634ea91de815c8f53c0a60ea06a568a13495cb693bf48a57441396c2c2d3d544d9ec9cd58cae74e5e1c482e9805692f48a8b1a7b109ded25ec703588f81abf14c85eb58c46af5c405cdcd427c4fc143760664379b4c985c38b23e884ba85506a35bd217ffd4f774a56a29e5876356d0da86866bb379462e264410172f2126ad518277aeba34e2c5dc6d148184b120fb3f6b4ed6cced9b3b7abb9dcfbc322765039629192eec990e10056ad5d8596c9f28c62971479f0962ed00ef1d1f20ec2d56a8d1d4a3740817be1f2a2f8b438553cb50f5e28ade5e8a14db26b01f55474ecae4d6a14feb7a24c2acabc7565e81d50b95ffa9fe8531be4a4f911ef713397e5f0e6ffcbd51f1983bb5af13e5c679d58148cb19909dda076c462008bd3b85aae1d00be57138fe6745d469af577e9955e40b408e7b66a5326f88005cfd01b65d1966fb845f814f7f176cfdcd0965e16c3ee970ac1c77a0704cd4c1b0bb2ca8baf505574440969b34fb5f043ca346c29b8f47076a7a8b5cc11308a2f90d44d4f012d6457bdda0c87244b894e0c01fda53d4b9537ac898c24b3b9dc07185072c7b4434bb92fccfb0bca122ad413aa473022c741b10ab5d03c4293471730ad56495673ae17fe625d6ed3da000f598e7e823b1d8bf42b05c632ae256e702045b74ac1c7591fa6425851092844b951ff89e52dfc93f5c34fa7aa4c32aa014faa5b7a3176d2b43212ad93404509a918160b07af25ac6502ee608b38f7e7859bf8b26680e49957717089370322343420ef2cc9ec9a73f45abb101dd7e9313f6020c0d7e6d2c9515d797bc5753bc8838736c24145362b3c17d8335300630862e2e19029a18320031ff911c63e2efc58ae5aa94c081a4e7498681fccd833c8c1f11b2763afcefcb5ddc88d66b66cc4ba8da0926138ff51b92a5ad1c477c10c62bcbc74267b49e8b91cf765b18e7bc43aa903079a11628b0824c05ea5f4b25621adf888045eada5fa002ef107913e0f3c2952c7ea54be3d38645dca5cd970e863456ba91bfa031dff7295cf1a7bd368b867c46a2126a31fc84e5f4066578b35a7da462436e121289b914a9c6e25f4ad7c720ed521f9c979a6758f350280946bbb14b9a9d0986064e6b6b41f312df142c8519af87fa7a415b1efd54d7851511d659e11e535af7f2fc1c18f131dbb4a9ac45a5fdd51f8e2fa8eb5c61c0b8345222b7504e45a1b1956ee789e622fc4a121025163134ccb9501e0d45b443156c63d721e0821a20eca2a5eca0cf759bdeed0d66e01e48a388a6d1623da962969b59f6d456a2dbf9959b04b4c206d108dfe4ce0bce671508aa5f7261afc0ea8ac35ac87180dfd51caf581c60c5c0f470498b8533d7e30aea32c175f1048d08cedac230c04fed9f8d9d066dd5899749fb529d8c61877e34174d13536c96145e300f380457326c3b30f507ca6ea8a7ae12b2d5b9cbf8aa36d522d63ead258b4be370fa6ead2edace8f55161cb609e9a44e9696166ac0895816c5a641f71f87e48521a197b0cab46ab483b4bfe2d0eca82f1a2c81b1001fd8908b4ed9ffcc26a351133253683b2c338961ad8167394ed5b156973999e8e873c713cb4fa4d17c64929c85e2d7926aa5d2427964100b5d43051eda62796a22296249b136a90112d492602bb684e5c8340b4543211ec4273420d12d132c924b08bcc89358889162493825d3427ac41205a924c0676c99ca8069128960562b8749898b0f095822752ad326292c422508a86886b158849251641293444a8552226935802a5c810b156313141622928454384b50ac4248965a0940c11d52a12134b2c00ca52501c0347e465dc0b395d8f97aa7a4456cd9038855c2af05a335465452a11b3bdfafab0653e694a2a21079e5035063669d7afd8a46d25fc2e3c7d7dbe8d2bc1b3497576d17ba84dda612c34d7d926ed4c0b1597caf53669d7feb849d38d0270f4ac1b63c8ebf35a25ae2716f2bd49fbcca019f29bb4f9504cc2483baa615b2a5c464926a0e93a98fd381451e9842538c6ba7c018529ad3869174ee3a45527f83a899d8e8f93ea042427d5850d0adf3e73e334386937952e27ad702547dc01fa8706afe29cb41bb8e7a45df93b71d23ef02fad22e17417272a807556449332b041d5d8801736c670af0a098b047574d5649920907ad2b3c833a346436ae8f8a6d3a2823d0abb694e9fcc6910ce603da00cd87211c72c5bf68a7afa9f3c7d7dd35231b5cc23b5cacc946fe34f4ba05f25e003eaabb8e3e9d29ef14e9c8c95fa1461dbe8eb739c7e8ea42fd313a329b871eb1742d9ff2f9bfc892bbfa6d5a8ec47940bc0f7438d75bec4ab1ad2baf3e224f15bfc4963cf502b7151516bb3fd4deffbe10a956c15b2b16de6ea42c3b613e466e6a80d70e3d8a053b1ba3d59d6b5eda70a97100e52154c9301304d834165496007be8f7a70e9bc07ecd8a5d6d8c477cb14dfab58c00de63b98484819e88602b986c2fad9e1ba62212bc129990396265bff35d2f492b04ba647fa7c0918c8ab26001544da2ae7a3b0fe7f508074b77001cda582f7e35a6f54adeef62e8c0aae63598e45a8b0644153adae11f4b644a8ec46869cb05aabc2764b51e0498124b5394652d915a5b58d8d3a60375581da6bf89aed73059b3faf09a9426e808d9538a0ebd6aa55986e298a7d29101237c44829bba1b636b15087ed252b507a85a8566541a95ca85113f6836cacc4019d3655adc2ea4ad12eca035138a1454cd90ab5b51583ba6ea85b82d12a43b92a054ae58a8a9a5527b12ef1b6940c374f6e0baa6de36cc7c216e7dabead9daf5aa8d416ce3826e993260bdcbf228a94b11dcac6463cd0f186ba55485a25a8b7ca80d039228e90b71bc8ca463494cb96ba45c81ae568974a8190b9428992b719d8c25e2c90d396b255303ae528974b805339428a92b62b10a78da570454d20663f8239ef355e334c58440d2723d94e7ef6021186528f369b91f86bcc3febe8bd8b9e249f7c6cf95c518ef14a6dac3d171fec752f569095148706025b1a3596041077fa33f90d290263dc9dda44fb0ab5a8a016b28e75c2e40382f8f7417408afea8b9f8596b84923b3c327957e5fc3dd3aad48056ce2b2b4297b1a358352eb30de177c1d6743d8803cdeb5809054100849f841056d13321c6ef881604b2c1d0ff9ccebb70038cf2c0e9f43556962fd7db64cd9219b0edce61db78d625bd3d707f48747ed7f7fe596192be904b399fea6028f81882bd79bf507515832181f91ff5c4ac6561c8ea33daf6730ae3302f4879834801cd9ffb385c820c24b04e2abcd43d12e40e47ae96b9fcb86a056852d5536ea3fa45423f22590a4d67524fe0f22f1ecd6e18634e5bfd97b9c3f7331a31979e0d8d96ff164951c704ce3793ab0afe97c9a16fbf6563a04d5a14b875afb9d4371a9b18cc028e50644ba2ec7ec9fa459831fa52f5718b81774d159cb072de9d4a0655baa4433464f5bdc136a6e47934326eb9ad97c0fd59a096ad5d140874c88ea7af607bdd87dbca01d90694becc4da9fde2e3b6660ad8b895599f630055927758cf537b4ba3217396a3fd94f1e1808897b43b906b239967863a2cb390576d3e56cd92a9c5f530f03ff2909f67f72a2fcd7143d455961eb15a4c5e7afd1186cbfe88ed0c90f224d0522af0a19dfa71061fb1524ed0d8992fe7fcac4fe5f9e9c4b14486894d03285f4e91429a5b2885461af551a8b95c66ac51eb9725eafe0044b82150beb358b14d1b2a66a61902dd552b734542ebe972e0757bd209480493e350c453a46f4426687942159cb941133d5d58c9f9cf941cf60099af0150d35248d1ca2665cd5f02d6bba754d2f858d8bb2b94cdab0b54d2471c345dde8296fa6eb1be602a77385d34be258d73857450eadcac9fce9915fe988963a44b48e6cb1b35bed702d772ae99dc282c752c57359f2e04cf324a67ae8ca1ec1ba6756e143553e85913e2db58f9be2e781fa41973f99f50f19024855016d2a810869a0ea22a84f05f951067de9204242285b09312185546ba1e916437c72a8503d544310b929a26f920892264a218a585591b2b2685d17d11346657494517def50353403c0bc9433696ae0a954dfb84a3e5f63ca3bfbb42f81fb8c14deb197de0e532a7905a19dae48b78eff0e3a807f333ff928520e9c9d465a90bab10f33d211d11e3d0178245fc9f4741e14858c9e58dba0b74ce5874faf08eb6d6fefa44602c65b441b5cede74cb3e86430f3edb5f0e41c55d0bdf49a3797ad1a4c191407f1e18362f7fedfc8dbb7cd50d2aa8f136ef40be0d4e9583d5ba8e8cb4879d9cdde86ffa3ae3bfdaace15b721ed4afc168be9513bff3a43409f480ba2cbf9ea13eee905d84a0d6c7cb5d5d15372c1e9ed134b1425231f799e6daabc4ccac21ab1b5685608d3c249ed63193116c435896f62de0e2ee730f7bd3f4761320f0534382c9edccb390ea1f85536dece8144d116081ca8cd7e9a8c15bb26b8dc2216c77f83576a51b306d333f335ad609147b00a3453506206f2df65b91dcde919e16f35bb1e6eb19abdb11e84fb1fcc902c7849a19f0c629121001c9b6e8c31bf094a92af868d5fc172b2580fdacce96151776d199293724b9bf0f7c6e5c9b20a03b16d58521c7373e633240c24676eb6ae61cde57cb1bc0e342c0f2e9efa0099c76fb015912a872faaead311ec2f146e149fc9b9b25744f5da0f816d43585d72b0ed589fbc6ea9418d5d72826c278a51d73a9202df751faeecdb32fe933d822a7b75c3bfdf7cae26b41cc6b6961dc49fc98b763f6502988164b2ec20baec8223c264ccc42ee1021e40e531213bcf73f9c6fa96ae78d0e39efd7e65b74b8107ed42466ae1998dc2841d51af5d6c6df21675ce4397a42e92401a22120048141473125f8c47468bca9e0042d89f4f16e7e967e4f789948cf94171ba0558b184606f46a9fd5ac33d4f4353288153cc41de7d422033a99cf13b3c7c53d41d5414d42eafb1784939dfc411d13697641deb082e85ad456562d0b65b38ad38d7b8c829769caa30360520b24957d0f2e8db339262069dede6b4b59b7cd39cd5bbb9d88a60e02cd3a1f9688682e2d3813dfe2e528e5fff196e0239dc0c9b2e5d52aa75a9480d52c414b174d1091addc5f19dcb5aeef0fd255d2e12554adf24d0aad32f59225c1d2d1f923c5bb0e4c3aa70a495836f2ccbb650055854868792432e75bdda7c65bb8e8ec99ce3bdf5fe3f32fd5e6f75f7d8f067b14633fa9d2431cfa1a661973774dc013317ceead01824e6c759b8589a08cdc6fba0aa52203a07f9baabef270e4517eb728949b63d4ff5c5dd9cf851ec553a1ebb29f161bd3324e4f5b4b3d52985fc8362340f4af2d962481c87d142528cd6a2c11c198c8ebf5806bba3df2e4b536b77d05c1925adba4a00dc6468820b278860183488105007220ae78d8f790e9111cfcd0412b3148751f3d76d562cd43dc3639120982792c117d9aad19612492dadb32b4d0f4484ae4e8a4f6aba78a6a1ae41ed2bd1eb28df0eca57d8f9a8631b4cfa4ea0fc92911d2d9a5e8aca992a26a321e38354c503a14a7e9cf01a7f016ada3fb1f069b3cbf0ec935878c8272098180242303b4b0fda253b1e84ca4ec48d495da34fc3d067e9828ea5bbc42a4c73f9da75a22c872584150829844f6ff24f4cf4f1483bfdca0e240d8ebe934fe610e9e4c01626c22fcea12204e43b49a6e933fb291d1aeee43d9723b88517730cfd1524a7a318d79ecd2d45c78163834b08527163a57a332b21310c9f55ae1a7e945d8d4ea73f0e7a056efc86ad6847676d516062e7178116ad02e6ab8020cc5091cb9389ac910e17df882363c5da6245fdf21734fff00d2f3f3a94fa4262aeb607ed46701de141c0b903434b0d88f2095d27b6b8b2b0288ae6883f0b40cade38a90f5b382c6d6dd61198a2e498eb354c6fbddcbad1548137ad88765e3ce2e5b891614f6e04c204f0aa26be503416261c688c33c70ff4c54190314f3c60ab6c185f488afcfbe9e567d192783ac1d1ddd4736f6389bc2e164b4a5713ff01359d6878c6f46168a0658bdde89ede74ea4d75d532ab7e4cfeb8529df1b5fe495a186aee10c643f8283646f222a00365fa94a0daf2cda69fecf517f29e9af5943b6d019b6bdc4df96ec123f37bdd34e29e2e1f99d340c88072d4b5348a379cd55abf674f62ef099cbc2cd5be2f46c349fcc38f5cf4bc24f8ec38843c0192d9514def90f9734b5e22b4507186e1083b21208ed42d141455cd03ee424a887a1cd3da2ac1feee94fed322bf3cb490c62ecf09f0ae361ea70d063f2d8b304160d2a37ce9415f2c4ff71f3f58f83f608ccd4f6046de5a76d929e3bf93df393ea388f9d36916e5f19a0714d910d3c968dea18ef78786ef2721550728fa307cd69dfde4efb6e20a03728c288303fc8eea84153f94d9c4f7ab836e83931bade5e2fd21f06bb10fe2003716286bffe59fbfdac74bfd8a496a39b260f84fdbaafdf71d0ffacc19e912b5de95a28f1b666a105eb9d5ec40e011fa34710899c8fc3f1fe03efda1461f671b61d165a6e7095ab60ef667b7f3a27109a41eac8bb817c0409c52b914dc917b00710fb8bdd9cc7ea8c5a99e9e1e8d1e46fccb717018454b0119c0eb72895ccd75c5adf833b6b9223efadf5d7486a5c1d19ff6ee7151f0826d16fa10a221ade0d44ff66c77fc1a325a1a309e5db733d80c4bcb7606dc6b42ba8980d0bb40882a912b13c94016fc38c0da030ce351d6752bed4c3d220fe5cd9bcdffd1bcfa668ae6d2135f15fef3ffe065e8a7b897e2c0fbc1bdc5e79debb4228be30716e73a467d1b287b4c81673284b5611808b6b38352224ef74e069f8f7846f6b147c98ff1c6ad0ef6ad754ad012ec6fd51ed735ea1e104c9a6f7cd8911801c716f946a0ba2b81a44bb425fb5dd4a3a3f7fe2f0ec8587fcce06fefeca15b78ae1c21a6225896fdc7f514ef76fc71bece7ad40d3a696243865186b2cd39969ba247028ef512e71bc5991b23c2add8e7d5493a01858c311dac9b7ce1ec47efcf63e4cce111de30738bd9e17736a1bef361df1152475210969354448d88d1155905d4ba4484b5c4d9c806630eb4ddbbfaafd09a6d21acc8f0d69a8efe6ba83bbaf7c4cba0dd736b32a93bf13e9f059eb2951e0ff4bf09f4c3f86e77bccd728ca2c060bf2e3b11da074338960e8d7d7d0024e3c71cc1583777893d7ec546b9072162698d1f2de9a698b0682a97e5b2b2bdeea63bf7e405841fb8b93c4fa388337bc08b2725369bc1799c61c98844716f77a51a7c12fa1c908ede8954f3f919972f81464fbe5af54e87cdc6dacdd2f0d2be0eab956d9c7036a79ffcc970da7036837dc04cb3c5f31ef48aa71ee5b157dee885273ce3798f7bdd53bc81f6f664f0894fab789a236d5b1d73b61416da2a66caed4267abc7ce963750b7c878d8b2547d4bb0188aa51ac62f7acbb7dc463bb7d1ea56b6a6c5b66abd652d6ca31d6d6975eb5bdf425bb7d2f216b6d1ce36b5bab5d6b416e465e8211f72180879ec8f64bccde4bf10050e8fe2b24738965d90d2e4c4d5719edadb57e2e2cb8ba1e8fff84b7c345d7a3428dbce3e80719dcb4d398f9837124cb60156c72139e4d3c43e060b5d06cd3ff69e5e30dee69fcd23793075d27b8ebabb3abe16b4bcedb80d8d233437f5abb4cb86b13b04c625fd80a56959285749e0fe66641ba69b54443f8f124a75bf5b0d43d23eb47a91a8b1e7344045866c9fbe48867a0cf71c77d7fe0d56fe36e41880d5dfbd915aac34056608d3b76de4103ffdf9b997e605d7b27abbcfe1f745d344311dbd701fbdfda05676dbf93c0fe5d44e01da858ef409329660ea0c649fc01c3893397166034ed6df61de6626d731682b78fb1cf080f0b674ce3b182e479a3ff4bf08827e0d41ea38dad4e38bc80f21db41cba133140ef3c6d62f9c04ab0d5e3eeeac74ea48afb03b993af442ebaf9d1d539ac8a3c03881e1d51424b99866b0f392811db731a2b0a361819b43b13b678020ffc445696ac141a28ad740c781a7f8e616ef06e448115c81dc23d5ebe498d07e0f0f2c12886ce237e93c2bacf892272eba3d0d3434525602e8ff4a11ae042840ce952937885967c76a363840adddacb93521cb1761475e0ea27f691c9968e1147aacad0f878dfcc9914e542c5a2a6110267b0906017d7686f3c493eb0979dbe66a588ba9b0dca809f5c4fd50cc6e5b40cf4b5240056235aa9c84fe030421696dfd82c0f702fbe2248308b489b8043974606d39334d6cfd9fca9d9c0bf9e497f9c9bb24694bb1cd345e292d5063d30ea9692452f29edec32ed9f5bc1548d044365f7d5fbd19b12eed23d8fb1b94b34b6ad3e09523a0c518a7c790c55172af2138373f6c22fc1c695bd791676e44010ab91b14cc87a2a27e68ba812ca82e7c102ca45c7b859089a8ec8ed84a37ae88d025d40012433331c533e8afdcb86e9993efe5b6d2cfe28071d99b2f1e8c352925493c835e3908707b210cefcf760dfc28b1a0a39d5ec77e5a0e6c31be62c88fc296a0460aac05729c9cf2a5d7e5a7e299e14d3d58edb872bfc039a6c4a28cb89a6750e70746eb8127e60dc9d387a421beb4849ba58a098f7d451174179b786a5defd29978705324368dbc81e7a8a7729b0be8f492490a2920e141e337603effd18ee2636ad7370a8e42742d892c8c80f0809e30b60d1d98ac222fb807f9828f8e531a2b0632f8e6675b40c162bc743a83d621506610ee0053a82c645fa68c33881136b908f1cc82c61616ca375e13bba40ddc30e6291224d24496ef775a341b669b0b674a55029be828341d0670facb68b9984356a0c5fc7653095c225abb201fe2e48804209cb12cac8ef9e6ad7c4b5b99ae9be2a7dd47015850c156f81186b1576642a3f593c19d777c37c24049daadcd95ececc3df9915a7a7e84b86a386e568afb0f81cca7eee1a222ecebf73b640ffbdcc83e3b7e236904f93bef1c40f4742b0af6340906f6f4bbe12ceff05a473a4fe3dc1e945aad64411b2c6ca48a0cbca024760e9134eb04a5038baa2b9c44dc8078e5d2d2be1362a5f2fd4f31fac997c40a537348842e366a65091d364b0a00311906078a0872b50d5ed89ab37438811d1b238a603dd085fef79522af9c1082c6688fde3e21e7c1334387b02d5de9fca2539ff7ed6e0079b1120b360cd760444707fc2a20f53f009b04f1aa5558439de519f5c20fea5345708397c3d2201bfd3ec9b2d8e43f9af679675de6c37aa1289abf9451132525c698d97d8e1bd6d3fdab6ba8c6da526b50f1d005f5f4761d8b02782a5b1667c6b470db7cfe1547fe63eaab30fcabdeefcc76b8334421c2c785811398160a1561f0c9dce0ffeca023dd1c008bf72f4030181027d8f114691e7873e65fdc074ff8d0167cc3811051e75388ffd464cee81ebbef9f8e88dd5427d8accd1cce9c9ef5ac9b193a00d92ad1124da16ee30271ac3e7118a5da220edb95d70e51053d1f480cf4ee38f5ccdbce0c1e07e99722f48407e0a76eb671bb423c9163316cbc2ebc4c6702a5c63721a446263bf904cd5fbebb1e683f026e06cb198ac3c2cd1cca5f988ac1b1e725d188fe738b830d4231ab5202d5dec724afc48bbcb1732dbbf425fbf03e968e85199085dc99e6a4727528cea924c13fe5d6bbbf17acbd519aca91f879f9e7590808f003e90bf302fa06904749e3a2f1428fd5fa94eb32bb7e6aacf05683396f769470d9beb31d7bda913a8d8f24c8893ca0407dbb0c0dd0a84bb3bd2fb3cf500b0771d8b5d7ea8192110e6c1a17e70e4ce1a2222c74b34ab71a266bfa35971476ccb4111937639a3617b89697e107f9a554b6ef7beeeb8ff72fda871c9a54e2ade207f936909f411915cc6451920b854dd47c0bcb33509a4b2160d99a6d6e04a6d7f4ea00d4e249a68d6251a33b6fda1358e3e439e51e30f91cec3003fbf025e7b741a0229fe9938c2ce7d0a87fe3bd1592b6bc082db8c9b6f205b7108bf02f38290310e031418adb8f0054048ec5c180b7c49cb8f95bf61ac8700deb80a01bb5aa428e8cd7928488e11f787ce5ac7e631c5a2a78c1593c31c1973b3c742a3234c602dbc2b3b41623d236664848a589027788ce015c06b6fef22ce0f6e7a9782b37f2d7d0c523990d1550408f3143065d92f166070f24dc6422f570e59ad0fca122d8e49db50b42cc67d5a6e56a46562d8538aa654e34062a97152c2a35d63871846e5324c06a1c94f4877a850a0b19329cf95e6fd796c6e9896c2b0866120fe0e152ff4e8db3ee4e36b5c32555d978a595c91b370acd4234d326c38bbc141ecddc5499f51027fa4aea417d620f848df04569ebbcdfa841e93db7d1ef7fb1ebc8a1edcade78757fa55012638c014ccba0b6776fac4a9f4611b63f9e6efe3ed74ab65ccc37137a8a8bdf110befdd71783a26ec8366075b9bdde6b646193f27ac893f2b127bece9dd760186458c2b846fe40e68c884676fc0144572669d195e01e7cdd580c4daa36c087939b66da6e4e592a13fa49809a228259e92f0903618c92ae0b96f8f9475fe8a29d5363de33cbf802d044016278a7161d85c80253e400e1f162d78261df1f3111cc20a0fdd31b68d50bb990b7bbb33f0e200e73b83162a0e85d57f862ea720e5c9a355a16d6162976ba7e1a1a997a2f74025fb2a9d42417d4cacfd2d06a2bea7b349debd69622d885ede98f979cec5e27d744ff7d3f34c362f547bd898dcf645dc1215034b446ea7a2585915d4b110836b3e6e0a9dbc23938e3c0a3a0b0351ee02db3c2da74a57b518fb7557c4f29851f74a84dac0be3d51e916d10d4da460301749c6d89cd120f7ed9f3a04ffa4a839bc6a78dc7692b45b9707698a94572aa7582422ec1aa88fa63fb23d6aa8d590652dd8565954ca1d3cc4119922f876fd3f5507f04a81146563a7cb6bb1df7d2e85ccb7f19017ac71626b85986327e602192bf23c88e653d6a99f10d71e865d96a4c69966ebe9e6f2154214067865286a526bcc9186661d0ec02bf4a345c7db655a942d11e3696e4ad4f9bb0a943abe5c9effba2735303b096b0bceed0054bb174aac0754314d968f5a0ec4d1b9e3e6485d7d32850deac12a108ebe54b4a395417f3c33cb093795e69c74998b5b5403c8af03b90c1ee9bff53a3efcb13587c4b3d469f9e43e98260c9a31142c3eca90182f498dfcf19681212e1874e8c7dd94f304cd5703d415cec4ee0afe38b7ecf04533550f792eb16228ea936f2b1b78b76bf7980c4407c69f8a96c9b5272fc7839653b97600bf26e33269d80a0232de87896d01815ef40541c895064e8ff221029c88224b874ab03da6e74a1c5bc0c20b0c2816734803145ee6ce277b8de5a6df8797ba3ca44bc38b4bd10b6aa982836bd062ed2279bd34338d6b287e837c91c0f4a13b87c8a7ad5dbd162beb62ed54e9a935b0ccebb1a0334d802b0b1686e07d049b34d9f86b1bcda3af24fc18d4a9e97e7ecdba0ba1b206911c740b6e9085c2cebad0f1a6d3a70d613c667b96c8a2d127a61b7d0abece9e28b5a955bca7257bd446170256db7f2d79c7d2bc4af5bbff88b1bfbe47d715a87d9b95817364e9383c294956f2addca44d55a1a692dea7c5165f64527aa55cbfa20df797ba2308c96ef278ff5c71e9ca6b8f3807b5b66c8d05d3c91ece6ba44119d7ca9055ce9bcf9c7e5677a79c1645b550e1ffde7e5f5773beb5f9a6ee1f84c0852a1b22e8468f82dda2a2bbbcc32adc3732c0d7bc9ab798e05265680cc94bcbc3e3ab44ebf0209632211b671bc573d543f5fcc47b7612b31e830674858dd46c986a3d14b65e10deb71d1f85cd1e2daba4a4b8d9975be1db4788531eac58f20d9d595589ddfc566770142412f7496283c109b6fbd2e6a1636a8dd63772f0c0d9ef4aeb59b13b2443aa823e2b993a8fbf70bf3ed9d997c11643af9399a53bf6c78fccacca6694cd77cea6cbd3f98ade9cbaf6ff6c76a29d73fcd4bea0b7b13e27e478ad31774b04f7ba5ca922608ff73f0b3f42afe9bc56e549861aff406f9958cbbe6100b1e35e91f1a0aa4e6e43baba2edaaab82d714e05211a73424efb7f9f50c1dbc1d39637a511365d67ba4e682be7f1b9413b7c5c4a609d930a95f396d066545019376ddcfb35d2bbbfce865e60658cf5798c41c28d3a27bd7aec3ef506f6cdeb26f635dfa0e64d4f36fd59713c55694ad6bd37b49cd6f11228ed3b3426fc12e47bf388c712f990c34d18b3b1833bf5e3e1ecae4fd81e082a5c9f0fd767bbf31fca3952ced4edee0cc14fd232493d3004999b3872b0afc837aa0073fa2da9eca57e523d61843b55efbc07a6fda3614c73a08ac10c32fb9c0d0bf18d41f986e6775cb7d287282417902d50f65b48021d70e561c7e2894def3774f1e6dba73ad1a7be8c47e661d2e3875c94e457e6b6401740080cf0d894a1ba515f07c60d5b4fb25ba1c59aac55e33a4937172b91ae0c8ac7d99004b3daaff098b1cc97d8f8c3759a94a2cbbfeb5ca2b9bd9fd764383a882ad87d75c1150003477fdce79fdf5d00f140b12c55de60cf9acaed23ea58ad4bb1c5af3d4cf19e3c216d1aa176f7f50a5c1477afa82c08b5ea54c8530f78cbd3d487c6882230c482fc38ecb07624aa036ba5ba0d03474646dfceb4d03b280711548a60b54a73b24a151b7018be841b478409bbd728bc77b1186ebf2641c12669383f13553133d4326a3f700e911a8a582b47f65e2ad7c93e7ed23171cf1af8f805f56b3b88c66ed1accd0a1c84343b0fcd54e1ca9205f2748d9336093683d7adf3dc4877320c09e9db604f64a3df684d22d68a9d1ef36d4d2086972d39cb8ce97f88e3fd82bce03277545d18d3c91696d76e6f5f151ba740bf4f58afc375b0cdf12cd3a4ca71d771a392c09e01d31492cf43ba648ae56e18f02124ad9d301f1d6b6692f9388ab564c0bd913914ad044b80f3cece413921581ec5be89699e0ab56d2c1b1ac1f7dbc9cd6860739fb605d4fa535b6cd2680094ae72fdf7e7a5636b9426009ddb0c3a09717a257f9ca49e481fa075d2f8469a86a1bd33df4336fbe10a256689d2cf0b91db73ea8ea0db06e4c37eff09f559846183a5224c0f1ae800d100e51b03b15f76a9aadf1e9117209784c8037e24f29a2f12f1fc605df6d861e319c3470fb4c40436aaed428f682bc962a023f4e3fe6f516aa8f13bb8a10778f19aaa1092b87acdde7bec77581656db2dbe60d51c9cd1a8b4121306af0a7991bb2cfbc3cf4bba74edfe935a73fb3216dfba5103156f530174bdc48a83f448cb1834237f141f54cb52f40a800fa6de481d72780e92dc46deea408be02e4015f9fab245d1c7005d681b1f918493a8b02ce542a1f6b664f7542578c158948a6904155ebd3692304ddaca005129fb327c2a26071bcb9096642671062175d6b2dbeed119c23b0b638d515b9b2054dc404dd3d7d95048aaafb2ba8094d3f6c1e36b9a613359ee6b6d35ec052d2fa5a4698c4d13d9f04b6c0d906cc88c690f66c79108a98989ca781a4be5c700779c893f7274acedca3c20cdacd0c80155d9adc7c5c956addae7e05711f8c37754bcf888568786f7a52dc9bf6238e903f9086f0e84390990641c55afeaf57679acd3f9436692c5126b4d7adb548d01c13accdf7f0477e8d1b5552b6be38209c9943b805c6c3d58db70b8920e47563a684f93f37c9d09b5e268a2ae538a9fba472c024b8986aa102d8a209dad70d17daaafb0e672051b276114b441107abf2c0d693eaefdd63f183ebf9e084dfaeefec891ee0fbe172be1c2ec047ac2b7cc2a4bed2f9468c2bde7c096587f957addabc314206db04c5a12e6641f6c16ffb82b5a58cba57693353cf64299f40dbde9d2d7f38857b84c273975f0937a883e9fb60f4c4f389ef008b506af0b0fc2d304e244a975c6d5fc03d62d149ac12c92f899417c2e64d12e21793e51d74e6e455c6d5ed66e0605621ec7c4fc7dd74b4dc47023c78abc4216a1759cadd9005e9e739b9827b37e942ecde5bc95f8ff57cdaa819b09e7ff21c47e0368b5954c33e11642a2d7382795b93f80e55e386509890fe8769056f5489ffcaa6fc888ea5fdc6450a2d1db1dc75818101d123adf5caee99728c87c88d409a248f94e2ec541300aff7794bf49cdddeb18a481b4eed252b9576836cdf35d001eda8e77cf61b8cc3200e261bd8ed5e189748126a0c5fa59c5ff5e6ffc22d23515a18382ed6cfb6a4de8afdae7e9d28a5e0f1863c9e1f99cdedbdc212819f00d5cab6f6fbad087b1a7ee6cad5f71beba79d3103077770931e3c946d25a75d170e2a0880167bc6fc26d013dcf5c40fd13ad7c9065cbaf38a6333aa2be3d25f24307d01d10b02c6d1815556765644a483d1c8bf5b46ea6f70ea65c4546aeb162f978fb00f343cc4a61be57b4429cb254f0ea3c21b4a62d922c016244711deaed83845c8ed25ed69b64fa555cad09237f5757c786ec960f620ce3e37a8c7b9546f8ac3d3da26f9d487e8acee48ccd268874b186e4c2d05a687adecdae1cdd20e680825ffd6ca291fb1091a8718f2a2f5ae541c02627979aa646fd0f70a6c563587623cd3d5f55cc03756911de1f27fac26dc61ba7842fbc50c3509819cdb735d85f61f69b02350b89c4a39a846e3fb3a1880bd6365fede07809bce1205e60bcaf707543b1c71e2b40c159cc63894942ec44b481ccf07195b469939c86ed5b4d927ab7e479de7575cf30134e5dfdcd7f0d405adae4d0c90cb0f060fb2932cf05bebf0cd7a28b9b2911dec346c951d03f837fba520f87b8af38d30075e432c8498e79917af3243a96935a775704ae4c3055f89716cbb8d913066127590b5c674c3f8197c2d0457aee02c6302a03c50e734b1682a7186c4f170db293e4665e03a89b17c13d86cdc2b79f3da083282ea1e008339a784ab93c231ee8fb2644023a4f002d3d77f46284ccd9a015630e172e249ab4daa0a89bf6ee5b20c572f0f252039b1cc7834e5cb852295d4818d266749a424ad520ed490027e85d408c5a110f7b0a2eef17f74ab25c284eab9d4cb2a2537061dff55e5ae0fb17867646bf42b20f9cb0405f6306ee745881f4a83d558b68ac4fc2743f551877168693666e23600b21a6aa40dae111116ade4f74509bce47886bd3057dd23889a0e2fdb5183be9e2c9cf78409e5e166b790c743d742d529f266b384647ae924f3bbb21159c7152174fe07c4771b35ae4ac65e7b305440c0ef1fbc5383dfbf29ec4bab7bdebe001c57b119314aedcca2f7a32d3e71ff22cc4cd9771fda989354d5a9512f38d4576370975d09285a32c440febd1341d40a6cda51911766044c99823654712cf87c53fc6b320d36a74a2e9c1baf8234a6b6b6f56552497e94887636beddc855fc375617cb1b9a7d1470be5e6c02ce44f91994ed72c88b7e3f3f0f046eb32f5ca960b9681ba9b29808b374aec5aba3694b0b037e6a7d750b583bfa05783d714c329b85f27e1248aaa72518770ce1f8d1bedbf0196f894a2ee51b79e9abc20f743201165e7f4f01f37c41745f41f2db4bc706054fdf75733c05abbf6fd1316399ee76cecc3c39e17d26f2412b1f2e97f7deb847db5771bf36590c4c2d298a9ef60c8a44ad986b19c1e119a0b6641ff166c68a59ded4b8c7504be8500d9b45f0cc8257cc2d28794900c3cefbfa19f1b2e718ccdbf31efee3405ffd3052dfb7e8ffc4f726c9834df51211cc2ee21b75dd60ad15d6118247e928b7b61c9895992768fa6ac5ed8e43fb7588c1178b93d1e6d83ceadc40aa6a1af4028c3fbda70725c022d7f701a20108a36f7f2bf46402e07a93aefbad0896ec7a6023c3af3fddc1c0732a5ba95076a8d2a5e19fabf62bbe58fc2e5c2e2eb029e6b8441b82fa7c417f4418ada475af13dd60d8f73f16ae51d67808421f768360a3a5f3d480ef3d35cfc2c126607a6305d002c13c3229555b44cea885e08e93d0e57491f54d9073ca705f6d18378a0fee1608e5a84e908ee0fc4e70720f6c63038c348c181760410693174068e91e77a28369ad98eb185d15170a2ee18ddaad8aea14931625e79b5a0d44b57649224e030fd4f52c3c501bef8343242894712ad343300a6205593c8b3ed02084d9599535f8d967a72e0e11723b9c799f34aa2b2f29c6d0c218189763c67f03d5660f283d92c100b4401d2170365eecd3246c223bced650274a37c3f890f6a1081d4fd09a48fd311758ac35d7b15ce1e75f1d2c50676fa34b6535e0c014a8630bb34d518d45684b1540f60d7a48c3c0cab56a2cb0a0365c87459f780cf7d2fa5b18e4368181d6688881ffd7da0838ce760807e351a405ea177bd62f16d8413db17656cdb11c19fdadf140a75f9175d9d0451990b9a0964d5db7594877f3cbd61d2a63c908b5403b78bbca2918d4860b3ceed76b28ce90dbd038e9da66f5bc3aae57c8ea3beeab0560a4d7f9f5b30cfde6d599d9b6252d5118aac668f36258c034025b107863996dfd15b2528ea5a12072b40456f6f11dac6f68020a607fe3f253c95121be2b17a302d78201317cd17fdd80dd2b5372af870de3aa86dbe6030cbd265d6253a54537a6813ab206ea596790495cd18da085a9d3ff4a082a9fdb7edec9a71006d80a690f2286f47c3612062c12ca0f3896c723c35fc1e6281d7f52ceeb5e2876a0a596b456addc9421251ad0279449d143d3b0343207ae60888ad1c83001adc08e3487c3768e60bf740c3c207de456ade621e3ac5d79886863207d88d41b9d3ea509bf4064bac0f8cfe96cb4fbd85a838f537bc2286b5bc0148411ca1516e87513e52c39d0d6a87e6506add5df8fdcacabc94f5c009ff4a2b7af4510fe1562642e0b8346c5208277404a6108744d6646320e22def0b8490229da1b1cc579579276d9defd7de89468d56fcf0368c06297e9d1dc12705c73300358e728992e9526d2f326997b4c920cb598eaac3f02a778afc4f58680803dc1bbfff9a8f023063e60de1af393146e11a53cc2d5c123004e116137fcf0101173efc03c6b05e0d5f5d82e1bcbf7006bf9a9a1950087b324d124e5804eb3b944919a89b891da7617fdf1d0036554b599dbf410d1ecf4315606a802a0b34050d32dea543ba12a5d322f638d1090047f03c5814fd95ebd6d7717eb0e0be04079660d4d5a398a0c602b85a7f8749f2ed684757d82c8ffd07d487ed38902a8dbd3c3d416633892bfb97a2ecfd11b031ef06fe5f22d46906823a05786ffd432f010a012f5006ca7b8fef95b5c21f9d54206c0af687b267dd24cfc851f1e3ab843e3f398bac24051adbd337999c760eddf06fc5f779460925ca5df1e4f9914f38d2cf64ce269a7b5f36cf5c4516f8baa07bb1eb5e91109464eee4516a4ab6cdcbcd08de55dff247a8d14feb11606fe5608fe2c6153e0bda747a32005bff3a3004cc9d39b670b0acdc6440d8bcc44d0ce02b8ef2bc0edd45aee99382434d51e658963b80cd00626c6feb566b255c960ff74733ec9e3c9b3c8c6bb31630687b916d134542ea0e19b2f0f97ac896e65d8a2504d144643c73fd0d1c9b399aa601f42cc4ebb26a00197d363a722066848c52b74e767771af28279491380bcb7874c502db0d5bdabcd58e671e33146e9a265501debe10e033bed2bcd035017bd0a12dd61f8b5037a2528ff611bb6778b9c6e52c2136595c590cf6da47cd9657bc34afaa97db343e1348ff58e387f53a042e20e66540d764badb5716b8a2d0e2a9860afc4adf83e213f5c4016da19e0c4e1692b88b76965b988acfaa5abc7a15ece64bb23529c54515f2f64a31be00176e6e7d4b0bb015742f2065b1ebd0ce2f273a320d22ce2e0571a619100c9f56b1f791f57e52c54793d42f7eb7ab5006bac792eeb5a669626b4a8dde122e57646371777d712ae1cce6b0037b202e0e261cfffbac412b044460f401b960ecc4fed8947d971c7e4e601836f595f0cbdca7e40fe944891a584c3b29096b016f641280cf1d12cd9e6d23bccedb1cfa2bb8b0b1f8579043d96e5c43821d8d0e0913921dfcfe2e31b1df65859a2dfc39e0ff5a1b17c43fa0f7c7a758dd6162eb2bdcd15b0605a573505f15ea3d86ec79f03e6ce1ec57850d464719e6aafe9663dd4b88cc75c79fe9a02d92c2bbdaeff571c22cc86c74f5bc27289bb5aca93721b6c3afb02cecaa836371601c33666b2efd8b04062d9a71dae8168e68603b223f88ae218243294b3197927cfdf6b2e721071bce1506a6dd647367eff7a10a3f046262df0dbf8c8c6968406735fcdacb4b43a520144d2700d7df3ebde10c704664d26a70a55e1029e4a85d00d3540b026c3013c0c3c0c3c0c3c0c5c751e8d1a7db2e224414a29a5a2df0cb4e7324a29a594524a089ff8dfeff73b8e5ae7fc595b9b61e10dfa0db20d62808058198609347231796ff4aa4bb4831a155c4cdc4ee1c24573b80edf62927e51fe39cfde4a4511b61e3e7e1467010d5ba85fd7a6f46b648911dd40a31693dec7d46993efdd7d1161db511019243d4000020202726802a8020d5a4cda54d197931419a99a085ba3922ff941825d018d594ccc97534725694907dd89b08d948484f485a1020fe8781f2a28a4218bc42e44ab756c354cbfb425d088c52495d44f5e5f0cb3dbc262b2c864d2ae2eaf98ec3dd697315b909d6b2fd070c5a4f9b1be58b11ff53e1b684edd021aad98dc779be799e2d4aa67564cce74196ae2d28adc7488d95982c62a26a70a39bf1762348cd40472a0a18ac9977f2da64be6fee37f200c31c2f040186284d10131c2e08018dd041aa998a444c978dafc74a98b172a26bddc2599694c895ad129268fca86cb645af4d4668ac91de377a523c372be420a41032b5779d3332a448a494a65bcbed83d72b33b8a49316f4c49f8ea7ad05010344431c942f4e5df5428267fc6cd94b4d6998a15504c4a2989ae14b35ba770178e018310015482c6272675aa60dd223f2c8893272626cb70496c5062cce44e4cfc4faf529a4f6c7b754e4c2ed98e99be7278aa4c082a29630323256494e1430c1090ee316010c2031a9b98a4b4fff552e34951cbc70404e47d204d18c8a1918989a72a747f7edcecc95a00020202124203139798dca5b473d219a3c88f2c31596676ce2dd6eec67025265965f7109b726d1d8246831e3a6ce04a8312067233a03189c9a952feaf98feb772878624266657780aa13f07696624265d79a7d7f20bb723484cf6302b1b3f27fd79d32326693769b6ee1d634691232698d4d0acd1a7648e9f464c0c192f7758ae0b1f7e464cb0d89a73ccc1ca2def8b98943e8f873fef5c8db32226e71cf3aadfeeab3a9b8889d61d83c5dacab89e44c424d1a9c4c79c36b3987588895fb23ead7e47c310134f8405bb1425934a9e2068146262beca9553fc8ffbbe126282ac9494deab2c3d4a3988095f567bf137d8b886829898d3abe51272b37e6cb140231013ef366dbe8d97d4aa31a00188c939a7c2a578baa41affc3c40e42894bf5624956657e9858fab36e26911e3be5fb30c9afa25a099d829acee1c324e1419fb0ca231675dec34437f9bd4b49cf35b534f43039f4a73af579ea4e4579989832e6dedec77bd1c1c31d26a795d968ba62c16bb337d0b0837217ad73ecb8c13e11b6bb22d0a8c36131c868335f1068d0c1408ac61c2668ad8e416bc5f5d2f618300829a1218749b2c9c2c89261e4e558848d64840c341a714864cfa90b4db33b0a19396a75773ea0018789e222eeb631ada90d85d8f581c61b2658675c1a95413dfac70d93425aca2addb6b374db3029a6da4b0b9b9a7236d93041e8cdb1e4d3beb90727202099041a6b98d82533b37bef359b7d76a0a186c967ae791a642a1841184a42230d93323e8d698785a6ca4781061a265f255d51b5ceea65d138c304215b4c4d57d031af98a170afb12fd14cad3a1b71a6d4c96c3e5662743b060c424868946172baf9ebb5a0e2dfeb4e8609ea4ec959ecf3bc9b02020202d263c020040734c690031a622883461876d000c3e41c9a1fffa53fdd3d5f9864d7994574f0af4f96f2a0e18544a30b13e49546b92ad1d6cf120434b83039560e953a27ab1b3f6d61e2cdf5a88e59f8d6240d2da0c473da5c29c534751626a99ff59f1af7ae74c2c2a4f4a2296806933f9bbfc2a4cbdbdf9dba39d7ee5698a8d1379e4a96aec2e4d1dfcdac544a630e53a1b49311d5dd6c1b770a13bc2da6e8a8d8be47a5808a62b59effe45e10088841383073048d284c4edaa163cb29d3174b284cdcfe521637d66cad4f981c2bed7c4e973e6a874e98782652de936a8abf8c44074f8230c4280389200c31c2f0814448fe03aea0d184c9235a7f99d2690d3262c2844dfa64f6c5f286679630e145c6b3518da3f9311a4a981c54ca9c419d88ccd968246172aad71473f2fce3c6d040c2e4a4316851b3179f3399000c348e30316ed43f59da4b5ad68d30c1db2ae7d54be249661a459818948e87d433f5ae9408136f5449d5a46ea1f33f8409d27f33c758d13b8f2c8449a373ae59b6752e59348230c983300d0f9a6e4bcc54a00184895d6ea16407f9a5a3e51713b46cb8126b71637772862f267bb4eb549b5e4d8bc66098d18bc9a15436b1cfaedd55f3629206a1975963b3e4c744d8cab2306317c54e77cbd8f46139ab0b6e8318ffbed964f25c204bbabce7899323272e0ef1d0f5ca75ef4fb936d3d9dcfc4dc6db9591f13cb08c19b79868ae95dd64790ed39c76c40c5b4c3abfb79cfc5ff34d4e34c48c5a4cbad6e4a1d6ee77c773ce42ec2c0f336831f1947cabe49ea3893b8b097ad942c611ca42288b2c267859a6e89a7ec54d8ec544fb8e9f9ef7fb2b2f2c26df879ae7b691165a55bf62825e4a71199d514c75574cce2a7df5a4bbb3c56ac5c4cbbe7e9bfea33c848c1593b359c6759d4c8b414782305430823040400e31631513d784886cf114e4594515933f669f713f99544c0a3284b436619b93deba08335031d1c4849af55c7afa924e3139c7cba262b83ced9937c5c49017a33da8687d9b9762d2e9109aba9f3332a498f01b63ca9f53ae908b4731419eca4a2df2fee2b5a2986c39ab534e79e3ad77a198142be43ee5ebfb188162c279cea92e65f060b27d62828e1a93bae9b00ebd27269d8753dd3542bbaed59d98a055b3f62859bf22837e5c8f199c9868232cb75f7d8e952a9b98b89d5378fd1c3326796b625250bade5cc764b4d2332313136dd3c8cf2996b54146844d7b8cec10030404f518d961181393b77469ea53a6513665c625269adeb5b80a193c6c3dc312935ddf66eecc77640c89b0cda8c424d35db313f57d2f22a36307c9e9814a7480d8d60cc20176600625268dcea1edca365f681506332631a9e53a6452930b267f494cb2b4d4fdb17229c62e12135fe4c7d4696aa262161293f3a5b66b7cd89fdbbd30e31193c487f56728fd95bde28889d9fe35c724328755bb11136654deeeccff7e93996004613062724eb2a3d5eeb75785125e0956508230c408a30364fcd100da5174d0a0c78f0e8080e88c454cb4b04fcbb94222bc821e2538ebf123a4c7081a0f1d5e83198a981caa82a6ed1c2db596444cfa146c4fc90e2262f2c58b5f624a9fcce71944460a40400e31d162d48fd5d2a482c544d8748044ce43ca781f3e1e64862126dcc7f1d9f3709b3ba910935cd7630a329b3cb3122126c6fb55939b46ff47348889b295ad6ae79db27c0431713f6d9397efdec6261093b3c7a8416a0ef7b117404c0e0f16cce2a56ca3e47f9894cf65838ee3a6d2cb7e98f041a7bdef189ae2e53e4c8a1ae44b887d69062d1f26a70b573168103aedb37b987caa796fbbde355ed5c3a430cd4aca64f230498bdc87d3b942c69d789834aeb71653aec368f10e13eeb734578a1c592fda61522965a136dd43d5f87598685ebef6e9dd63262d0361061d26095ddf50717e3b4cfc31630e93643c699b2cfbad9545844d0e9384908b8d9f17b623e1384cd88bc9939d8f97103a3dd08e101f0306213f980187497396c32a09cd196df4864996d77226bdcd8a39e386492362438ef586186969c3a4203d778cb9ea92669c0d9366c66c64e9b398cfd730514f657f90391f2ab46a98f01e52c4e765ecbd9b8609a23456d67d3f1d648a8649a3e4fa3ea96b3113ea1967989841ce5f105a4353986698fc2642e78cf18eddc1324c3eabb64d1973f8fc906162a510232e75b45bca44d8caf051d20391f124b80ccc18431647b673ecc39ca533c430c1e4c83caf753f52c61e3a5a49ca8c304cf8a066dd325ffdb80d860942c8ec269d437e6bfa0b932a6c26d774756ec2f2c2c4516feac4ebc9fc761726a76dc6e54d7a93d09c0b93825992e964d2ff55da8c2d4cf66c6951338818ab2c1c66686152fa70315a327d39a7b76146162677921b2d29119e325bb030a9468fc5edd11bc5d531ccb8c2a4a42c636dd02654a9a408db977c7fc973e0ec665861b25c8a5a5a45e768aa7915265690617378326d3acc549894791a4fdf4aca299d30c408430523086324f5c02dcc98c224dbd34153735516a5224618933054308230426c86142608d58abf39c551766f14265690f2222753e7389d085b57610614266599509ded251ff624c2867c9071a685194f989c92eefdbfae94d35cce0e339c3071574ea7a77c71542867346182f88e391f5e5452a7313dcc60c2c4f55152e39fd4e0f14340cc58c2e4912f7b4a2977513f95303999eeec746de2bab9244c34d5de763aa76c1973f447186314fc11860ac228fa837d300309785da6be5abc9ce78ca595a8b13c5f1fc4bc1b08438c3034108609461046ee13661c61b2450dcf1fba4cc6934548fe036188b16c986184c941db3f6e8618b78b75000561288f19459814a672cec164e88cf30969f38148b00233883041f59cdc6c6a39655f4809a24109f2c08c21941942987499936cff505eda99118492ec20a7839062af081b83a0113380f08b89a32fc32fb5083399b730005f4cd2a647c9142f29a9a20e921e23cdab89778b01f462726592695feb27674b5e4ceaeb7062c673fad729c2e66503d8c5241d47ebdc3b65d4e5ba98a0920e7a97bf4da971b530805c4c3e8febd87c423b5e0717134353a9fa48594b1ee54b27f5b5ff17b40d8501d8626212ea3b87d0a2da3607ed4007492d267db2ce5729b773a5fb0b03a0c5c4d0a562cefe460f7a76c100663141994eef778f7d623b2dc100643149960ca6d34e25c2d63418402c26cd064d5f4153f0d660192367773000584c2e9d3934cba9150b721136927cc5e49493b66f557413a2523118802b26670eaf9fe2df843ed2e3ae1513be62b6facd3acf3e8bb091b062d25f5fbacd07cd503a3dde9ced18c02a262615378a755e69acac060350c54455cdc13fb788bf6b253d100e50489e2e25b48bc52dd6312f9eb2e966627a36e7d47136981a161363aca791b3255abbc221e6af98a4824c9a8330e53972bb62f2969818359fa7e43667a31513935613f24ac8a4339a6830b2a391157c388f11cd1d47df9174156a5d988cd7a5a7c1c80e54056a9b6916ba26aee9a9e5f1afee238f8c8006341819011923638080d0606447a66272c5f7acd325b7e65134021ba898f461545252c46d92293e525265a79814cd74348e4a62e456820d534c50ab15e743e5e87773889561a314933ea5a084ae50212284a498e81d5407d99a49dabea368d575e3e5446b66743cf3ca6abd7d9aaa38896262c89d0c32a6fe4abd7aa19864327aeb6792b53a0f28267e8e574751f9b6967e6272586b17ddb451d47a38061b9e98a8c9b299dbee8ae9533b31c173f2e4196f7f33a87062a228b93f99cd9f146c6c62b2c836f5f0a0cd2ac8c1604313137f4e4fac7583e65c666272aa9f7de75920214eb081898997c334dd7509997de303f92526a7ffd812a3f4cb3d5b4b4c5cad9227b795a27eaac4c4730b4a3e5b6e8312934da94ca773644ad9b39368cd36674aa3b1b327892badeae6f55b5eebad45785e511fcdfc6e292fd888c4a4586e9bb26853e5a56303129336f8d57b4ee972336ae31193638efdeb9c62ba6c77c4e4cca829983e1d5a4d3d748484ec0834b081128cc0036b42d61a31a9f2e39c7dd02d6d7a57c1062326a750af6019c39756915bc1c62226865fbde88648ca7813808034c1862278d7efaaad5313b7cb41fdb4621561a3811d0f1b89381b88b071880932bb745493a7de2463884949474ba6f8762126780c9d31da290931e1469c38355ff6db978d41b819a223e75af6b59bb1b2ae5fa162cc10521b829818cac3764e5644cf2810134f7f5d3e7c65396f01313964435444c7f6d3fa0f13dc722a55c26cd5edb4e1878951f635f4ff4ee9bd7d98d49a9487117363bda70d3e4cd04e3eeea929e6a42718d8d8432b23b69eb19d9a76af9e237e2ca87899ea7e1b7a98641f26c2649ef330d9479b28ed156b117294061b7898a8eb9d4daf2ac8cd66e30e93afaf479508ffddca4418052e031b769874fea6cec6d3e468490d36ea3041b78db50695af946c9a0e136efc82acbfff168bcf61725f66edda8c954f59351684c1861c2688bcaffa933e9faee37198d41fbf1baf979f628a2a1c26b867a9b47a59df3f3a7a6cbc61629261e27a964f4c8c6744a404914007191ad05638d870c3c4bbb652ad8ce1fe82011b6d981ccbf2be9b55c9186210109215830d364c9072eff13288cd7c695d43ea6153ce6fc212820d354c900dd2c225133a63f8692c68316ca461e269f2d7d0f9f5bb6d344c4e412be8f40b52337c6c9c6152f86d18d1e6e3f5296698a02e26e55ad2df64b836ca30e1a388b40aaded394d324ccccf9f964d34853c4d0c37d818c3841f5f8f1dd558779b1826aa9d07d3ad98a196db08032a36b35da1d7baff316f29265369ad0d304c0eea4644a898f3c55049c3411a0ef00b93773fe9d3d0f726cf9246051b5e98f0419e92a696a4ad076d7461c259a95a8ad2d464bd0d2e4cf014326557120dd69b1e36051b5b98bc99a67683c8a7c6181965a8c00420203e46c8682ed8d0c2c40ea582c7cad1b3e56601084848192567061b5998a07bff17df6e446a8e09ca28010129a3e4b660030b932ac714f71dec5684123fc1c615267cc99c8e5e9add632a9960c30a939469f5b18c6ea61e5485c929997d0c9d29e869850a933a63b66dd69655f9a630c15df478a794aced62529820b53a9932a52c79a6d20836a230296adbc58b6ed053591b509838da6fd3680cedd5a5274c3aadbf95b2ad369c305135e7f7bbe95226f61f4d406f079d7de7ebe3371326668ea7ccf3df9529ac6309ad9ea7c69cbec875ea67e92dedafd9e2aebe49060f1d684309934ce8b014e522d5dabb62230993f3ed52ee13a192c14307be0d244c38d335ffe8977310a1085ba7921f4ed23e4a48106d1c61a208f531eecae2a6648ab08590e8383302eb16aead1df6a61ff26f1d6db356d56ea30893649f8a499e1e4d514f84ad53c063c447077c9490ac880d224c9011e2b3bb6975eb48842d4d078f1f3d6c0c61820aa753989dc6d7f288b0d5ad0d214cd878315cb73a9fea13cf6d046182ba700b7263752a9742464ada061026968aebbfece19f627ca4447f31c1cb2d8d7ac857c5309b2f26bf8916b923e3315767074908193f6c3032b2c3ecf4403c90da19f76252c6cb964bcec91cd3891713be428d7cce116153dbc5e43069167bce749097f9ea62d2b8a76c16cc7f45e823c4b5462e26fc9ad0a4e48465381df3514282b898a083ce7fccd1e12928f92d26a550263b3473e6ce1d42628b49a1e91737bfe5a6364f0f1d39e8f163a4472d2686fb7cbe4e9ea4c69cd342abb8af7cb9aeda1811d395e25af486b39854b953d0c162aee84125c2a611a8218b496a43555099c2950c3fd588c5c4ee6c2fe3779e9303d4037d2011e3e3510a7c94a4c08c7d9490c022d32e2d1bad1717190bcf1964fa7861f459ff8a89e77731e3b7c7ac3929c276e88ac9f9fcb372923921968bb0f518e1c163c4848c9484f8692b26574e11974f08dd5f325a42a26304ce8ac95e9534968e9ed44b57ab98182b9eaccb1171997935eb121e491593bb72c51053c92ac957478d544c74af9842e8d3cf7bf3bc094040487ca0de31601042831aa89864bba6d5772f97aa94534c103336fa44c8302a42534cea984f2ae9959b2e25b42dc5041d47d57f475d322debe08080063d1e052020202024a498947745cf96997dd01611b6901d3f789459a0c62826e9b520ea528e5e8e1511b6cbc10819213b7ef0e84a1d1cd240c7067a79fce881a29894257a19ca736bd87dbc880909193969eaa384241413c7afa2893a7929936b74f478138080f018300849354031493d069349d9eb474d89b085147e62f27a9eb8a4f477ba213b762093353c31499fd2665236bdfac9ec18300889418d4e4c3e35b2d2d37ee56e6d0320203b7620c38909d2f3857fcfeae1ee31a2a3e4778ce8c041c8f13112d263e4aec62626dacee5efa83da733e203ed20a48c1282031d2704af84a48769627292cbbdaeadfb40c7c7c865a0462626a78c97f4ecec8fa5743950031313f653c4d25d6c44a745d87aa01d21a7070a4146c70e923cad718949a54efe9b6b7d5ca7c78f111f2165e40b6a5862923613b3bb794d8f0e9bd701da4a4c741bf9e0b631f55dc81809292141253e46444842dcb499c78f1e9498986262949fd61204352631e97d3ccd2e28158d9e2426f68dcafea9b1484cd039e7e52cd2b3dfa52131d13e4c63b60a5fa5648f98f41eb7156c2ea57dab84c4119366d367ce8ea7dae98f0183900fd468c464116f39641af51c743aa9c188893a32cffc74b02cf5d869a0c6222609353df2cc647ace7d50892b625257d660e9727f5b3611b6f36420488d444c52f3b0d5983cc6da5084ed6a20626210654a888e1ed6def3719e8c331fa8c40f3131c6f7f5f20ccf51b2089b96f0480da86188c9315e0a9fcaf54c7d4834e03a060c425c50a31093b2c924d4c42719d4b7226c253a402134f851d009597b1143c23e5ec4640a6a1002cfefb12c3b8f29c2d6673f424a78a41ec4a4b0e9e275ce66a3995341dc5a752d9b55fb5a7939bd2fcffd31468d404c0eef61ba729af4db527906d4004439c6e8a89ea2e978fd1ffa1cb425994d4f49a7fd60dad31aabcb7d7eefc3319c282f753a98fcf0a111dd294f5bd638ef1e26f5e53e9d4f5f50979e1e2695ae98332995c7fd2ae78c78d7c8c3842f1d3be820548ebd321e2696cf9c59d22eb3183a237798a4a14fcc8fd5a699e59c113b4cbaf479c406751fd4a5ce6bd461828aaf1b839fee986845b8e4078f90919290a6c3c4990df931447aca67cf6172f588d2ab205f41fb7715a821878939e5f28d5d1f952a2907231c87491beb61fbee6ecc54233c467c906147811a7040a7d5bde65423fe59086abc01a1e4aff397452b7fdc40478f1f21243ab60435dc3069fbd6c5e35fd65e4e1b26e5bcc982b9f6874da7088f94a0b1013bb578759d640a5b88d151f22524676b98983c78a732d589b0f5064850491945c78041c8d550c3a48b395c9fc5a072aa1e04a4e4c74819bf0304a4461a52d5d58b9bd58abdf67450030d933afd67fe54364f3953e30c1394d4db4d22e46c142511b6463e46c4f0f12c281b483b6483df51d60c3e5009c90f1f212109a8518609ba67f2f9da62fd77da946b9061e2a735755abd93d0a9c1a0c618d668c232060b35223722a82186c92d9bd26ec87c083ac31a6198745629c7f424776d54030cba555da7ed95cc5a6ddbe630ea7173cce14711b6103e418d2f84d4f0820c6a74610435b83029fc64ac990819fd926a6ca186167ed4c8c2c44a1f4ab54b4d5f6c45d842ceb68447da1a58e0313212b2e3f80809c151e30a35a861056cd434eb4bed2a2de6f27daafc246f999444eed4a8c2a4e8b549efe5b3f76e6b506192cfc9e6efbac61426e58a85f5bd065d421d86187e89a8218589a397da95a3a73639310f35a230495909996c3e4b782adda10614267c967eef9cd46a4c17421d30448d274c1215ce56c3e59cc457c309932e9f9b8587eff890f550a30993624a41a84e21136163c224a5d22995428fb80eb741516309934fb6851cd1d1c414c2aaa8a18449225d46889339c8dec614359230319a8a90dfe13d9dc7bb14359030e92ad9c8d7cfd2333a4798609d4d748c375beaf6166a186152d5fd657511f7589d6ba1461126875abefcd3b7db681261b2b88e9b98eead969ed3506308934aa3a96895f1196a214caa6c2d2a769a4d9de545438d204cb0cfb2a4c4b4652165a8018409a6d59684ca2957ee2c038d5f4caacf20facf6f93aa90fc382134e8a123a40b0d5f4ccc0e71236553fcd3ac1713b3c8f153a7ffa9a6e7c5a4d361445dd4cbe2fe70b4a33cda51feb44063179335e5e5d77b59d35517933227ed9f7f0d1b5e0c46f3bf3121bedf2f2689cbd61d946953d9cf1713b32a8bc69fc610f1f5628287c94baf2f2f26a5dc9fe7d1b9e3de5d4cf8645f555a429f6a4f1793cbca449412131aa3978b49fee935d987da28eae162b27b8fd267da693a79b7981ca39ee5080f9f3d79b6985c49ca66f711a6f7bc5a4c507a3475b2dfaa138f1693c7b3854e1ae5c452378b49b9721e1d4c96bc5f278bc959f1d4ea7ee6f9502c26c9ccba9693c72aa1038b4932b24a8a578a0c3aaf9818daf6afd4856ae6b862521255bf90316695a615eec6d7daebbcb162d2a68dab7fb6b5cbdb2a26be8ef61d9d53ff7a53c5a4a8b5d1f4ddffdeb6544c8c1e9f4b670b21621b2a266fd28c4184670a22db292676b0f98d29bf266dcd14937275b8fc167574572bc5c4cff59a326817254b23c5a48c226485176d14937392318de216a264268a091f3aeae70c323e6b168ac97ef5bd295812793aa098e46ad5b93f85d794f389c941d3a5a79745511b4f4cbed12973de54ce6be9c404d59c3ffc5ffe879c139334bd8ccc6fbce97d139392b21c6390cf3e62d7c4045d41fd7d4c9e3dde9998205e594a788e10d2c684a9ddba63947d894942580c4a69b6244cb6c444f9987477cca65450252686a8b5bdeb205410293131ac879d8ea5e47e6e129375e6fe6352297a563824312988caa1bd1d72feb42e1c9198746183a7c62fd3a79533c50189c915db4cae32091196537dc4a41dd3335762cad2b939081c8e9820727af5594e10f8d0114206491a8e464c4a31e6ce79d1d227a5c7c18809da1a2cbe2f874f1eb388c997a2e71caf9f7f55e350c404bd41c7a4ebd3dac850098e444c8aef23d573fc226c213d4674201af8184987768e884922a47c32f93afadd1e62529a4f8def3bb159321c869818d3780a5ebb29e7301261e3b18598f425da4bee6d9a8aa38f944e88495b6234ba8cefdacf3c88895639eb89854d9e16c4a4ea18dfd4dae2c72c8bb0ad5e2026775b8deadb77dbe8978990bc0809700022c40038fe80c30f38fa80830f38f6700aa6d2856605a51511de000e3d9cb66df2556e19bb326757599f6e53b08af330397736946e3cf5551d3c4cd89c493f6f89731ff91d26d85697b65f5a3b3bc9e0018232364042f2260001311c7698d841dc8d0aadd9d4421d3c0f17018e3a4caeec6bb9dcd483cc3e1d26a6501e455eff68904d0538e63069b3c9e5942b68c81549e43049fa677ebe3ad5351a19f1911dc011874969436f8b5e0787c9da59932795455d724dbf6152e7a07d2b7f2f9fd1090e374c8efb368fa73c9627751b267e7a940d4a835adc2bc0c18689b152f8937741e6aee86c0d934653e7509f9272918f473b8caa615278480d57973c698a49c3e94ec9a446c3643d9972fa6e7a8fb7e238c3445321a372dd63860927dda486bd4ced201c659818ce628aa894c4bd2745584b80830c93cb4c86caafcee86e978500c718266c769edcd01f4fe73ec490e8e0f1011c6298b4a6b46465dd7a1d0fe9f051922dc01186c92986fda93b0fbb72e106c3047129e61b3ceb83e40b93c3aad43ae6cf8d4945d8120f0e2f4cce7d277adce5e53492901292119110b437a223840c12edc2c40f25366d0a5a2b87100717266d540bfd5e119971d691521a083ad001903b1c5b6836064b2144838aea7168a1a03f89d9f3aa98c5038e2c14a76d5fea6e219faea3b4550e5f6943888f0c706061729f29954d46882a9946028e2b4cea154b9f84dc32d794152627ed7c2532da559898b6c1327ab638a830398e9cd2bfce62173f38a63069345ef49c826549704861f2d9979f850ce2fce238a23041e89f68cc65b5d0100e284ceaa45e2bee06cf24354f981c5b1eac4d63703861a2a98fd12e8511d9418ea30913f75c6348ebdad54f3361c25acea2dcfc726d8e630993edc63db7e90d87580e7028016780230913c5c4b3928e9b942c150e244ccc8d41ebb9df7d8ca3234c4eca57d64afca8a917871126ad66bca08416131f84701461b2e8974e2e426e8a31a903f40110101d20118383083ac03184c97a9dd25a6f354f0787106ebb38378d4dd554cf2c6dbde9e96e5387ce03471026a5df1863b6e2e779cae200c2e498ce736c7f99d022ff62b2bca55852d495903efa62c2a66c9ae654c66449d38bc9e927db4e65d3f0991362bc98b0fea9dc3a635eae2b0043b8b18b092b7b296544250fb1eb62c229d9d59e5bd397b79584a497c78041c8046ee4a22fbbb8ed3075b1719dc70d5c4cb4ef2f555dc1cb64d08d5b4c4e497f8c5b22b355f0d862526caf70b329c98ccdb5b054b6532e2b09db12328d4af9b1d06282a807a1eadff2582a89b0e140c7410e789fa11de5c7c8f19cc5e4bf329d155b1b7d39594c8edea35a834547082af990327c94a8dd8845315aa4854efaf7864532c6a5acd326b485f5156ceec876b57d587c5605a9a376c3b4a8a6b862d2e76f1b7db27c2d07b56272124a88eb66f80ba361c544cd5a22d7298fac6a5731b162f2b0cdbf30a63396851baa9894ed2ace85bd0faab420201d6268a93854dc290ce48629266c8652f1e6c44edc5c8a49d1b26d0eab8d1493f37b438cdf7ec8b24731d9ce345c5c955ce89ce41ba298a41d459e4ad53cb5b00e9088f9b8118a492a5999c8d4f4bb3d45d8ea3070031413d5b3ea8653aa3f4201c88d4f4cac3c115a1fa32a6f1432521242860e1b90b827baeb4e9b911bb3ca9a2dea22639e15614b23216ce7110a90d1b12305de89c936329ece1e8bcb41e3c4842d71dbae9e8305f17b8c9084e0d08d4d4c4e2a4d484fb93ecf7567e7862626eefc79dca4de3f8b6683e751968964b436c3e2d5e332dcbad4c7fc7a716a377e72700313933ebae9bc7b34bd25bec4243badbd6e52369f6e2c3131f45c582c994c8988e88c8851894959273b4476d753da44d84674dc0d4ad88d49d8df90c4e4ee0b96abb6dc333d9140c10d484cee381a5f62b9c36303f325c46f3c6282855bba18eebee188491e2b3f6e48eff86891c1430708c8888e1b8d98ac31f7b5eef79cae20464c4c8f95577a54ce56cf22eeaacacedc76ed76d750515ad3686b3a2584aa8849bd1a6bc55654eb06256252cc612e88f5274f9963861b889894c35cca318f675eff7d8c74c00623232320c38d434cfae06ef9b18225e97709508f12a8e08621268be5e8b41d552df3cd370a31612cbdb6cee3a950ea921e3a464cc00191121d2db841884921a4c6ccaa8a16d3786310133683f618fedb9478bc6e08625285b4fd18dd39540c076272eb6f6c9efdd6967a031013b4bb4b8a9a76fa70ff6162ba942dd59c96694c2ae1861f2665b9a439a3e2c8afa03e4caa1695f770dea569e4c3e4d67ceafa792bc65cddd88321466b55ae89f82225254fc60e6ee861928cc9f64dfce7fb5c3f76201f6470dfc8c344f9fccf996aad193e11b6901ca01e6b1bb8818789252d84698b11297913a448c9939165945cdeb8c324bdc92d3c83ced7f10df13112d2c30702019b0fd4638c1b769814d3cc28e9f122a6536ed4e15a97d735ed0e4f8b774f499adcf8bfe13f3c831b749874a65e627699395c90089b8380808090cc618287d5f2689bb229a59310077070430e1375c43afff8956efcf84068dd37e2304198dcb4392bc60a9b8829791e3c74f8286983c344517a3799e5a47931e564dc70e3a8d8ce12a5b96172d5e9bff9f8f04159da303955b4f22b95fb394d64c384f7ba98b2bf7e94bc2d6d70630d13a36877f7947354cb086b70430d1354f20a22d5db63328f6407193c74ecb89186893127a39f2a6dec73d03079548c07d3b7614bc98270e30c13dd2ade95ccdbcc6cea91f13c900537cc805e5a778697de75c73a9d4aa8a464744598878f910ef0f031929ccb3031ed28b154957baad7126e906192df5b57c8d2fe98496398a02d74d41453fbaefb6248c7ded6f4f3d435deeb3fa95132c970aa4f178649957b4525bd496edaf80618267b3e19fd2ddeda4b24c2a68344471bc90e1d253ff40b134f44d496100d1a4eef85492a4b4be9d7b8e3a2eec2e4a82f6ba71d979349b930c993d78e5cb8aaeef2c61626fabca94b21c3e2b6a38549faab7dfe63775bfe6f646192e8b134a394c509b1e10616265efcfc1d7e644ea2fb2b4c54d34fbeffb92abd5ac6b76024072ac80bdcb0c2e45c31e6fe6861c9d2d2f122628080f4d0f1bf49b8518549d253e5ac6493f0cda1c2a450552e27740eaf4c99c2c4ac1861aa54cc6041e68614269c88edad4b31dc84aab870230a934a881295cbb0d81a45d8722170030a93de7407a56b5d42674f42a2a3a4c7f3e8324a4a900f32f647193e700c188464e0c6132679e718dfc2fdc46cf2861326e8e916cfa21d316e34a1b356cd9337bbf7ac4d4deaa3568a656cd0cf0d264cd027b76a2a67b58ab98409e3f9f5ff35bbabf336dc50c2ba96d2ebd75cb98b4950537a5132e58c93f18284eb36e33c543e34eb44aec3d2c95a1a994157b8718449d23c8f38a5e6a22c7ba64698709a3ffa594ae95b471f28c2a4a815d46eba30b9eb032db84184496936cfda6dd48dedf8c1e3c61026590639cf327f1a9476f5560002c221e6831b429894a13dc7d7f0ffd6aa08db19e11182031d87576f05a937823049d6d6a9f174f97f2b3a401ff0817230328231601012821b409818ebebcf82aef86cab8d5f4c3eef4db7709731e515615b565f4c929bf4fe76ab67e38f7c24482f269fdfe5cabc1da47950848d0495945142f8487cfc1083049594b101e785a741d7586767f9243676616d8e8ac9a6b68e7a76d88f9ab3089d449c4c176723179362f7375e6a18519363c1062e269edeb9d9caefa764bdc5044b3bab9227b2be225b4c4cc992bc68139e63bab560a31613e3c99c637db61c9b3f8d820d5a4cb0b354be723ae5dd7ccf6292a7f14aa32d3ec52e8bc9e1729266050fd893809904f2ea1680b7af58ca1db08002c10631059812921f3e6804a08c220132720480c407c27101121d647c8e0044210005a0000048480ac80c017cc91f101a01f8923f233e406c00600008c0e10385d8c07c094948480100108003d8c091a30d3872b06152c533113a7c16e13efb71c603f908c1818e1372dc4a7ae838418e35942012354c3a31eadb314ce5f62ac2c69b230d93fd92da3c65ea73a52411b6e58178680e34dcc871068483111e3c100f1b39cc40068f324cdef1b4ae3b192647f3386697bcbc3a1721f990102f493a4448140339c63041ac5ec45d6c5c0b43427ac70f1e58811c6228830cc303f1a891230ca8a3c48708c98e90101c39c03049fe7fbef5eee5709a081bea28c1818ef32324c471a0e3fcb8408e2f4c76cbb12ee728f56fb71726c78a77f3c9c4c64a8748c8c88e921e20e04096f4d081243d7494ecd0f1a384f8e01c5d98d8f9630919511dbeb339b83071bc846b7dca31b32e97630b935b36e93ca295f3bf1262245a98789fdd4c48bd2c236ccf918549c9932eaf24fe1c589898e97286ac129a465639aef07a4a15a3db9ac30a78a7d6e6b866d7dd9c65a70a1fc35e5ba5534b0639aa30b94f87b2b51cfb6352a930f9f673327d39a3f79a7230220608480f21c714269dda85dd985cfec3490a93b7b34fdea9981c5198a8aaee39969272406152feec9ddb3dcb821c4f58c4e978c7bced4892c309bb5c5859c7975d7ece94b4be7fb5d89ba30993d7f2eda79c62d01921811a21071326a77c41e57ad6980bc21de458c2a4d3c92e07af51fbb1b3dc831c4a98d851ffcc36e48bced88e17428e244cb6d79c2f37689ff9377990030993ccc2544ccf252e57ef831c4798789ee1ad4653949dd2da90c30893b48bc91c45ec73438e224cb2ce7932c8ccdfaaa5d721071126e9f6934b5af392d2ef1026e81ba9d93125b1972308c81b720861b2aa8b7c05b97ae8283c46ccd9be2047102667926279ddbb4b4ca63980304956bafeca24bd1f60fc62b285f498533a1f4f3ae68b49e2f4d56834fd25966a1e80d18bc9b1f4e7b2ec7ad4b401831713ddc782b668ae3d15c1d8c524cf417ebc9710711d81a10bb5946b78ee051dfe008c5c4cd0a8a923c260e0c2115715113b33931bf1b7d624a2d5bdeb1d01080218b79874399e7ad217ca846744f89490849041a23801c31693a36c527ab4c824945ec0a8c5c4550d9683dccca8f905831693729a27b12a565be95d2b80318bc999bd545d28111a343f6e80fec70d26258bc9a92966f9c8b6bd9803462c2655f077d1fc2e5f3bc3000c584cb8d1592f4e379ba5d22bbab47aa6846200862b2606f9a6544e4237dc3760b4c2b194395b75cd8b4bd74ffa2f299d6263aac060c5a42a7191aefda664bc51e0ab98e431cf5b767bd1397b554cbaf93925cbf3de28cf3700462a269ed5bdc79a7d5f52a36252c8072575e3af86fc24609c6252c51fcb2aff76db9129269c86bda46b3ac3e94c29269ab9df5dfef3187c6d0318a498a4f252859754bf2ca3514c0c3f4a57cc38f3db922826ed79a8ca8ba70ffdc108c504b99ec4b3b9dac5e5c100c5a464236a5352fac4c418cbedb2625ab89127269c8cab8f59e7826d3a31e93e7da8784237c9144e4c32d5215c3f672b5c3631e1f256ca99d9529c8b26268ddb5b4ed92d5df69998e4773ae3da5bcaa5c6c4c4b4943777d0fcd7e94b4c529b7d3732c5302b5b62e2098f513fe6f49acf4a4cfe985bf99483bd9d9498586a5b57d5941a934d6272e526ad2965929818adbca2f87e529f484c4ea7f3578f6c1eb712242647d151754a74885ae9119377fb2dfe9a902326a7cb31efd26f12c2841a31b12f78a9c710232685c5cbb92e74d4f42c628232d5274633e68c8d22265dcabcc7cb9ed197444c4c974383eb4751b1206252f8701b3ffbeeb47e88c9e5267cff63c7ecda1093f3ef68cc9fb3869a0b31b1727c56513696944c8809961ae6f7e5fe843b88c92b3f67a61573f114c40411ebd4395a92ed652026a68c9733b2dbb33d04880933d2ee4c6ea8a037f487897a57ad7bc9d7fe427e9824b2c294e7cad677a13e4cf6ab18627359e48ef83061b3e8a052aca98da13d4c10eb24727ff35b85f43029284f2a4b2c4cf9270f93ec4fe4c5f3303b193c4c8abb389ae24a64de7798bc315e73b3ada9b7ed3049870d9541c347f1b90e132d9d4edd163c7498f09a93dae92725763c739860a55d2c6f3e990f8f1c26580abf2946e5204f3b7198b8512ff6e9ecc061f2e6532193481593a9f38689393affc9b0561fade38609da1f75ac4d3e46511b2669183fd5a2ceef446c989c5984a9d9144748cf1a267ebc606a339ee9a5d530e9d4068bd1392c9fd4a461c2dc07a52cc86455aa41c3a4dd145937b3ee5e9a334cca6d32055119e3a768cc30497df84a3907eddacb946192c5e0afaf5d757322c3a454a77d27437b4b670c9365749c6bcc9c23376298bc3dc2f24bf86756c230299c854ddee1eda20d86c9495c28a1b641ea69bf303184fe56f6991115f5c244ab8b3de2dfc3c660172698fed41cae64bf472e4cda949545bea40915b730494dc7183b3e669b2b5a98d477a593dc14198357b230e9a38fa52c9349cf56b030e12ed445ef346565e50a13bdd4da2613fafdaa62854942a6e85126ab5a4f5598245a2df662f01337a2c2a4f2f6cd1d2a77633485c96696f246e5aefd1c294cd41c7ee293a51cad446162508f1923bae2661e0a93d7af94a88eff22f5274cb418a12f84ed84096fb2bd2adb7e1a7113267c578cd0db2c7d3313267996d239b23955bf4b9898f97c7edf82b2e02a61925690a72c2609136374db93c9bb682b1226e5bfac9f43c7ab108f30f9747d49cdd42aea1961720c9e74a8247b308a30717449ad4b4184d0b107830813fb75dfcf76e497eac118c2844ddaeb84deb7f4ebc110c2a49f37d9ce77139d03461026095f3f8f7bdbba1c308030f992998d142b3be9f9c5e47b1df7ce29e2b3f1c5e4d818a4e7c5b7e0965e4cd0d8a0f205f9ba5ce1c504b1f1a93a263d6f71dfc5242146b95c7f8eedb7eb62f28d8cd8d4f531d3edb998ac723af5a9fd8f2b3b2e266915a52d6b7b8bc915c6d2b52b4dc91693baf42ad4d959b0d462825efadfcdeb9d645a4c1611b7d9369fd7d4594cb0d490212d4453306531713d7a5e3ab99c3cf4584c92d12d8ae851b9ba048b4977414ba8cbf7a939af98f8e29fd55c73d218574c188d1f1ef4646cd4b46252ea14fd6a2eac98b027b4e767cb2a26e5e93953131a33a6553161d39d12ef3946935331493ec8689d5da333544cd6a01b4e5bb89cad3dc544ad5232770ead6a32c5e4b8973bdbe5ed84a598a4fe92aa0be25f5e9262d245190defda79a1514cec2c9b3d7ac797184531497bcaab2473a79d178ac93e5f26d7e2658a5983626227afda94b24c531013e01313c38e909529e489892bba83066ddb39c67462f2dfcbaae6b8abbae1c4e40c2a07e126cc42359b9868d1b3375a5c4c3ac39a98204a655388c7fc7de14c4c0c99c52dc950bdb9c29898f431efd54afe2b7cbec4a4dc19530e25b6c48411b77d298d8c92722526a54f41cd534c9531ca94986cfbf9dfdea9bf294f62528a26eed736fc6bca9298706b29691139ed2d722426ff26a1f92d9dd5a5181213f6f286cb1c4b07393d62a2679bbe123aae7dc91113ef35553c6daa31083562d2982ee99936c9cc318c9834bec98447cb222686cb274e6c5bd29a57c4e457b98c7742058d3f11134eb66a3ce6f4b247c4a4906e1d2bc61c3cda8798b0fe6bfe6a1b6292dcd84967adf33571212656b96f0a67f93ab44b88c99fd252c59fcb15a33b8889c9daaab5475bf9b98298182f76ba9dcc4c991b88c93aba62fcba2c72840b88891b73c65c1edb3f4cde0c374f953286d7d60f9374f8d4a2f43255ccb60f13747ec372f68bc1abe5c3c4ea1cb6993fc5d4b38789df9e35b6a585cf317a9860e6a9632c95ca65c9c3a4d6ff8eee29a246c7c3e4929e57b6bcb46f7e8789b17599f2a7d50e136b533c345f554a5aadc30461993cc5c5ad3eab7498345761f229e95019d5394c4ee974e7a56ada56550e9344fbc5fbd77fac54e330d1bed5f45d985f4f2a1c26bf657ad80ab9d6a7be616289e6fb6eb89799ea86893e27548acfeaa14b6dc32431dabdaabfaf5d54364cb48b41dbf2829cdc740d932ac860f952a66a987c9552d6f4207ecfd2344c8a3bd72a4a8f8a3e42c34453bddfe14974d89c334ceadf1d69f163153466989cf5eec64def8367ca302957efc84ca6bed7428649ff2947d45fce1d2c6398b47a6a3acf736b55c430b9e3c3af09a5f3c2cf304c764f5f492d891cbf130c1373505bb2524dfc68f30b13537776b4e73ffb35bd303987b2641b45c8b331bb3031a57cb39663362531b93031a72d75496899095d6e61722cf97db247c6b7975a98243aef4793f935689659981c3276ce8db6d4b8120b93d2dde96cd1a7d397ae3041e50a97d496ac30b175464e438e27fda9c2c4312d8ba5c643e5870a13538afd0bbe9fff54a63049e9d4d1f598a1b2450a13531ee1397b6d66ce4761c25a655ef89de8f55098143ddda612a6465f7fc2a4cc185de9af652ded84c931a86ead92cd96e6264cda974b15c364c2a45399a62ae9281d2f61c2088dbddc0e09a084c9f741c5c9d196112a244012262993a17259c693498800489868f9f2ee8bd28b318800479828a2e4b252e7110d11c00893b42a645cd7df52320428c264ff14f54a67d3541b0210615295eef0ed63ba9a21c01026255dd284f60c5a2f4f00214c3a21dc4b4bb8a8dc1f018230793ff437b85d76de8f004098a483028d62a8b48e0523a2401c0c8402814018100a20ab670400a313000000101e10c6a2e188485c673d0014800348463050322c22301a128c0663b138181004428160280c0883c18050280804c2dc8c3ace015d6cefcd66e8ad4181d1da5306d43d8dfc5c104f75c9ee458db528b2ef2232f41deafe957a21f3f026d350b911c8028481e8557b1addf8588dce9ba26b257e5ceb4ffa1d712d00560d7f736e5e16d3ec1b5ea5e9a3ab71618ca02b411f1a23c53c432418987091143f54a38f93602ac6637221f49d0f09bd0dae49c4f61524200a5de12f2ad787c0d14917128d44ca24a325884e9c0229329f4aae91dc1e0af051d1c0aad17246d0a9cf02b12ac3b0b14c78247ac8514428a763905ca06f69731eb47159275b88092e6fb89fd427c7d4d0978b2e6a8f9dfb4a4d51d74a441914f1ea6a01ecc21f60fd76cb4a5fbb7d6b9ddb3ae564fb6fb96983063e4d238c2db2bae010b87af812b938c800578fd300cb58cee4b97362c2331598120b434a8f3dd54c5d80b994150366162be96ea0c76b2711dcd377002a3986a8a1392804712db69b829da6d4902ee089ce018482e803280ccd1cc52108512eab23628103efc0a8a923848e592a17003648225b0a164068f05c338bfaf94f54b6fc3fde37f38ee68ac2d4f0762d1ea2527dc34674d3e2fa50043c98e83526829b2fac2a74040c82357da1d5a2d94c3a633372c19133446eb68d5b41e7a1a38690eed5378620a53b56a62cc09dfa00f0822b4d9131ec4796cd6c36789c82234060c135c0e02f4cc274da4df366db50afe71c66283d74b68eda83a2623e812b4c14df42a2d79db9794b8120700a9711a5bd6b9206c15d246f8893fb179f1b2de84713a7eb7b5acc571b2ccd7d11a6c6e183e7a52aff2608da9a0e6f08a3e764d3514a83a8e0a2a481f056e25148730cff0283f7416074a22e7e1b9a9c5398e90fc0a359868a7db295ac6f46d547cdcd54ab6d94e09593e88ce8e16366906bf3368d7d4e21f718c75194ef6a9ad12e8e3d6ab4a1e26eec12c952a49624316590b9067a1300c79c40e8301d1e026bb2cea8f3479bb4a55dbe9141a27a8652f4d8ab13713853fcc4bb40f953ab818c3980f0085586f487703bbc2efe97142c3e7c1843165b68525a8ce33369764459b97ac38b614c9212e9297c143e4e82336570687057463e7fcee5858f7768537777ced4424f85407329c1a17948eed353e3609e89cbe1ea9f84237ffb9daad987d9514fabb143dc43644845b0281afba69a51ab025ac6c09c47c4f572520fc58d3c591c83337f1d2efb733722fa2219c4becc5edfcbf43939a2f7e36479b4f03e1a60eeec2062ca44c188e2024bcfe69c9ce940e9fb209fa20bb4cce44484f42159292667f47a5c96e7e208dcff93c1b13f73eba24ce42072fa51b23a4667e8fa5ea6cfc9b1a86606ce824cc41e8627e1d8df06bc8f23a72dfd5e5271312f149e3240110219cd13358c324041a9c245bab2a7cb68699e1411f92a889c002f1439ad8112c40f29fb633043d5e1b23707c979a6c0fb5e051b945d0db66d59f8398ecd1232cab3a6d70825955a88ccf25c8c03299c17e693220d141dec737c811684ec089276b820de89ce28e9f7df46129a842903b52d73c7edb63ab56a660e36fee3ebceddcc0ed6e9eae80954f3e43d47687c6882a41080d4ca2588e6dcaabc110b01d1a0abc6c5bc1297c389fe5d053960ae153aa71f928d18c9409d949d1c8888be4819418f6543a4110bb5ac5017d4553a4947f6767daa064fc6948b2a4942a4b483d4101c19aa70c09249b7aa99f4bdb802c7ff3638ed8fdc79bbac2e571769a825a13e0661a4727e7ae5633c490db1d281f4271852a090ca18d721a01f5236d31cb9e45ddb6b37110aa05a922012da472a0942862b1757e5a41e89db9e09badc9c0e148891cac987b0d123291027a91d5da55193622379a39015e055b8f867ee70bb5b27d6113a759a1925747988dc137624753db23885ee1346f87a19927ad0ae37e7f37db95f54bd9c6fbb1b893a18bb1a8b900a0df79208855a27ec72d3efc84dbc27c1fff99fdd440afef84afbd63b98906e2fdbbb29777440c3038442b153f86283c96856a97b3ed81ce59a0a00d725d4a8167b41b3c3b3828b36d41355d87dae8e87f3b7df46affe499e68ca420c61005865c33f8dff3fe4171881792bf91ed4d0d756768bea1266b0770547bc2c47e0b3e13e9abc6ac3ec908cb31d2a198fc93c3187d1c17f1920e99d0026265033ae103e76e310319b70b4391421590d6da7a45343bc46e2be6c77aaaf37c6dc798c150da70caf2a4a5d4089392360f6579542f8d1d07960b28d63443676ff38a6c99d00c71fe8a45d7a56f93b4d5411e93cd7967e940a0753098edf3dad41be21c5a730c024f6b6d60082461b6a345d8d92a8b4594756408fcc13ac0e0f9dc2a1a8e8a95df93a7505b3f6b6d0d2d9702c087d94e6f111c1a0ff7a44a3b9b0814ee3f0bcfd05ed249293fcc8a6351a79ffb1a6dc313fdd8ec7f4b0a132ec9264a090e1a880705d17ba25b40f849adbb93ce1a066c60bb45240b1787ddf04e87022c147cb124a290896c1a8167c1d576c0e2471e624f806a761cc931432313b9deac129ae9746eac4c1646d17705426abdcb441f4f1d767ae42ebd6e0ba04281ee21ac27280182c89843c7c933171b7c94459e023abcc84c8578a0f91b10d5073b6fe6368cfc2a273a17110dcb47db3f5d3ba2f706e392caeced9277ac93a82b81383c64fa512567f213088443eda2a59e3ebe7428949ca524e7298256c0c3aeb95390df69c73a066d512d404b2122514b7957e4148b1e6d00b84a80110d560dbd3e5f60fae4eca1b65d5304898bb4a8a31102b159d9086354234d69e5be0a9447341a269be00f128fb71c8bbf2a55b0438f98106fed019a7b5a505d5d5c7eb7f4c6e3b8cb88030704bb6b088273060845bedd40602a60aa87cc4ada05adb2aea397c4b805e3cd4a3e0ab681a5b0f283e582708f18ba4006d197b351bcd7531d2e5ff0a1589f4e3bc697c81ef097109ec9c4724d1f5bfb7c7effb3d9bbfe92abdac9e663c0c65d738b4af73001837a65471876c0065ab6b967153ad6b87b765465a356582070d7735d83a20f797ebe51b0c78bd515d751ccc196a092324ef9a804f742c496bc3e7dc6b2efa69c24b7a1c160b9216742436afe3329ef4722c54864694568698fd727837d2e75c07d296071b2b410dcb1c6a1c5649d53b85294926883b273d7c833901ae1faabf41170e47f4cea2a5868514a32cd50147f450140720c86a82e071a53ce1be308d9c5a10fd6dbc2f087283d971b2840bd3a0d49a20e06a598593e1a92170b11a1e9fa26015f81b24781d4876befa529c2358bd0a753c0ce0e6b77aadd833a0df3cc958d052433fc5bbe8681a684c43bd558a535596e2124fd160e3a461cb4555e70d4085477446fa230c4a4b48cfb791461c307038160ecdd7e12404efe8ae0f4b714388c14c7b2470eff826a22c18446654a8ea536f74009c742849cce32f2b58fa4ce272ae2c789997deb4edecf9910b7e5460f1c963a3d434fe0bfd19899c26aa02295607da39b88d9fbc9bd9c9568082238058721064bc073f7a62631116a796b68a3ca9e4480b3901d4a390d01df1e24f3ba6685e56adcdf57415836d764845b8ffb72f51cb3819478b90dd1c0ccb8e514c923381abd94e4aeb11a2d60a2a55bc5646ea596e848876bef7bb5e4b612e3d49c27e263c032b69423640def1c3a97f443158a4b30612a5daf0da4a14d807136ddf09abb97b3362a37f8792f5ff8dde651b2483d8a6837729078d24d4b4825f2c1241b8e9118004880594e529586a885ad4ff3cbe40a00ceea7da3faaf147c65756ea4f86422ab1fd32e78e0c80b2a151d2dcbbd9ba0e44a14e17589a1001ffb363d28ad92d0b0049970cf803680457f6e3bbf768379062024cd372b77ebb314d9bbc634607012aec177a79847e1d273137a1a60cc2eaf77cf0afb6a6c9aeeee34758c6e1ba89aa24450c04f18485f523c297f3a71a30329f51ef981bc94195980508b4a73fdea70ef240d950413c5a3ee0f6008958ed4e8fcf5f6cada2c76a839bfd77d7025d0234a63f84da93249ffa3f433f7884d681d5fcaa0ddb4ea79a234887fd8273f6aa7f2f3a88a50d8e02f0af895eb665e040621ead8836afc4227b6887d8a58e9d2d84330434028e100695119541596db44739059c964ec2ab6b4b0cf8a724fec3cc5110f0fd4eebe4804086853c5e66935bb72624d13cd267037091aa1d9794f738e25d1bca18ae81c0c4c9635e59b06e6afc30f3db9975fb3c47f0558ec28ea00e5b2acdce2201d6401e9ced523b5e643415722313e437870e75be2dbc8e3e015a7cfa44d896f008f859f0e1bd8e08efcc13fa7e663e95624d099a111d59dca9c00dad633153eca51d64d7807339c140ef1928f2ff90997a222b52c7f9fe0ac30fb8c014347076fe475010f1530d2e691d614380c37981250830a6aea24783bd245c553aa7fd2f4c0e5d8a035f6a999adb36e18db154c1092f04c7ef0181aa45e2824b8efb39d2527f202f530d17b02608d536a16c2535a507dc4de220efaec6e43c68d277bc5ba07d4912e251edd121401921ea74427108a0bfd3578392f7594236e1f12733ea3f94e6a2c264306c14d85e1f6cbd2f801ccf8d1dfdbed58f4028ff7038571b885f24855b911226d95b7be25c061effe52c0772b7b22f9ee3e4f61462f4beb807b51ed547dc294cf82a800829c0feaf851cfc2780ff3df6b23426a0a31b69f92f40083946ed16f1e99f71cb9e2e16cc381ce807159cfb5351ff017ddc137d4713c4410c07a36aa142846e11ecf26929bdc4dd3cdd02a03a5daf6aaa47c9d5813a02126073d5f27aa82a4b19e11d5b22c040b5f236611580a7d0065bcd2f430c8cdc98d1e548108d34943a9a80888800b3bdfc99652ada3aee38fb3c63c16348a83b79afe9e9264b0ba4fef9a1e444ca5f1de061cb18426a3db46018d0a203a8736d201ec76fc48ada0e4542477a391f50011a758268d557f38a708b67a5576c7727787e17a694158fbf2aca46620cb279983b1a5118d705fefb6079214c67abeb4874a52fef90680a09968397a01e23dc713043d07ca0a6c5b6a7c66a22197ade75c144af18212e18892451b05ccf90b2a564286befdb1d02cc7131ad641e6c166554054a0513d0b25c312e1b2acb2c2becf7b9f39ce6322a4b6bb9aab9b21e536295816892c3be2213818ae1a0339193acfa51df79a9c0aa499d9886b5243076fd2b06170425d6b1040fbf088a20d4051351355b2848294e63611435480c55439bb01a19bd5ecd0d1f0be0312e54cd883b2b02b1082ec9d2cb8b264490aa60b79c9601bd0b0af628e5ba9413d8a2875c6aa8a8c9c1490c1381996659a4518742418a6d8954d05075da07238390a73039cf15167b863d8392e80fd022171318d17cdff3d78dcc22a18832e81a4f4438926bb66ef96a7dcc2c396ba961108dee98fd05091c4d196a0405c7ef538083b399a6a96d8c7b3c73b0d514c28746ca64d0b3699a8529248ad1722b249573d047a9786cb5cc183b3272e13b2062705dba6252a6a2a3b33e417deaee51564c665a8856b4f6b1b6018e8332799b5071648946f12f457fc3498d56dd9dbc31131b9eea89dfaab6c6a8be340c8f25881b340e05de8212be80447d57352766c64ec5527a9983f7921fa8f74974813575fb8ada5c5229f4ec88976a42cfdd12852ecba179ce4a2bc77e8352d99ba64f8397d197a02aa1aeeb698c09e88bb5feaa58a791ee37739ec6a8bb3215d0b90593008280a451702130ff1a991dd129fec206459669f23eb17710a94a2abb132b7e6416ad46a57636ffa99b1bee6a202eeedbdd191845a177da18346d215ba5d700b1ea14ce26c174000054491a8002fb6b51524be2a7aca6a65644b0b21feb3fc58a99564213277be7fa8e0860c7812d9e1387990a960bc092ed210cbdee28391e41f8e4b32a68d45562b8aa6f18246079f58b76579106e98fe5be281c0093d49627b96249781dc6d9b2db024f427e7c077168a71727a2d3879fa176a3c79afb6e53ce485fd9210e51fbccacb62ecf411ddfa9ba14a0c9867964978aee510d811f079e673ecc440b21580f24299c9e1729e64e2c1fe474e041fc54f95a93808a2129e59e4f42492484bd386b359ad92c063bf9622ff2d0233f0d117a9e0e3f50d06b34fb3f634bfc33bd723bbebc21750d37139eba63d31524bae1160145340bcef3f243270d01b1ab3b646853ed9cf15b9f352860f3c3fb90cd2d2dec5ff2cc1c8c1639bf4be3cf5eb98a63b53190608a1b80f2eb7903f529cb07ce1230bd136c1a2d40a393ee91075847e8c98ffbebdc6063a4edd8be2553dfe943b559f04e3e921512f5705cf2f24f211080f9ce9a642bc193af5e02a47662797b0ed1686ffe173c42d137847c38f15ac3f5c7a2e082035bfa3499d4782642ea4fc44f1a11be1307dd835478b97cfcad231fae3ec655707bd39e61cf097061fb74c424aaac6f6ba206c75b07308880370d7b9a2aebf0ccd520c1c642d06d71e99d41e1b40cb830349f45b7bba3824ba545656d92482814a678e512e7c26b4745a2eaf66552cf31127b1d4882507a6b6f73c8dc725bb7e31b90b1b5ee9964df8f28ff0969c5bd67ed87a734e3478402c5ddc1ff1aa402291db31034e7f475a53892792d054084fcd913d890c6357755b74931173f09ace6475b937be8ffadabca6956ad02e6218be9a287c0e21d11da0e27010b82cf00206a111a31d0fc621e6d2234841595ff7d1c53c13b96e131c7a9bb256388a98decc3687e39e841556c79d2719a4d0d81b3a8965f3a529538b5b39a966ac374da3262dd5b868ce00326236ea10ce5482856813527db59f51e191a9273f24afc7b12dd847ed082907805b4ed2ade98922c0f7c66a62509d00067237676cf96b0813d89f920acb3f885274e6afeace4e893dc9e0a86a130a5d7004536043ae4c33a7b76ed62f3237598fd59a8898913a7a4ce5e4074b43f066f5a7c20875f7a097de408d6f09855d3c3cc24ec1edd83defc973f8ca376682191de86b8354d570e6edfcc7694651bbb94febaf056e25c72ecfac104fb412c48ade12d79058efeae386e71c6f0cfa0802f020c67bc4fcaabba10d9eda641c0c456dbf6e0e18ba1123641d31fa7cfdbbad0b0f1e0722417e9d4877fe0654ab1538d065030146ce1f3ca3b227460546485943c144e7b63c9b0875eea9cda66226ce4b1de6063a37f45a2327eafe5476f21472a64dd647f4bfb71e724a21de027b8096992850075091722c50ec23845b2ab390198b28ee64c906af530e12108b4656fd237629459f03da108c34e6222ed74f51b280992f622588a381bb4003efe1f899119f9b7dd202ef81dac419740b7599e2c9ee732822709977f69a4adb91c7abc3bff9cc686d0d7c8fe13548e6b01028ec04c3660c28db39946f493b89d7e5f681b8213aeaea89ae3e1def114c5580ba073f205b21e0e85b41402842ac91d89af616a09ca22edbae0812f60b2516eb053e400fa4dbe63fd34c98d478a4a1f434f8b30a665708d4c12d458605ac1b2babafd3899e5a048312c5ab9f3d7f43331f4bb25f198629702af2ea9b19206eeb6bc5a93f28b8128720c6c1f55a3e594d5cb33e196b418d46de3300c157c7f24a13ea13a7dc12d181c5e83c109acffbd232dc6a449730018b47edfee3850d0773339f41d2f50c06066a7c544834e8397febf22c1f644be9fd930faa93eaab9684d6e50aaa141951ec632ec254e72245cf9fa45c31cf1284bdabcf2acfdd04d1885918b6bcce50f5e599d993655ed433d841a9e31a44dd354c31c1f7805ad44079085f5e8a4f2a3a576cacd61f1609d01bdc7ecdab2a0f084719c37a4f6a6362fa6e311a943384ecb4cf93a5aa6009be40d83dd4dbfd9a02eba28c75480e32616b4ebb70ca2b219d3ba25eb3e72beb3e09120e3c1d8188bbef0d31a074cc672aa0a53fde59351df76571e389cb359e94f9f216547f82d204bcb8feb6c3de07c0fc9ed8a27b77df0684be2010a605ad0a27a6f5fe8ef23706bf9076b370fc3864f3ee053dab13e26fa44ad3049980a9ef58b0797e6623704a8a2eb6e217b00f05324a4ecc3f832319d15e1fe5f4f43cf0db54dd0a045ed3e70dbb0fb156f8727a60a187647844b25b981d05ec55a723104ec1c23ac5a62816bbcba99220e49cd114cd57258ba1fa71fa612517cc90fc2c0f2e8d87b84fe296625998e6aeaf864eb0bb10670d5cdbbbd9684538d5fcda1d9df41a1e12c570e4a774763e0e9d511ef0a20e71828476b03db2ef6db3c5c50d2e464c50e749f84aa4b2369cdbd315f675310a1c8202f511066e90d8bcea5a16b96ffc43a6d605439f8781b37819de677c96a01fb6957c2bc11273474695dac888ff6d09118efe125e950489e2be312da0bcd999726149f3cf15284f68063ec0bfe3a0b2cb7edf18a71f4b80bf733404ace30c92384a08dd8782226f91c219a7be5376afe76e793b4de4944f2942c225a55a35721be880b84e72253d309a10b4dd380c48e599cebfabb1c185aa63562ece4325558c1884ce4897041f47092b0df29129136e4a0285f830e385ad15d9710ffbc0c4256c0454487c5680f2853856caf2d822d81ec5b1b4c22cc18015861d8aa768e81a22489b9d17a060b95cb3d18167252125431932a80e5fd392798ce55750ffcc3964501919f813c6e1cebc2043709ea8570f04344a4fa2e69bcc64410400cbc5794efeab6215ec4f0e923376c021540467da5efb41daab987ec1ca1012c8e62d5c7b954f90e32e3acc99dc5f24322f5d93be462eb72cdade889c8b4e6bfaac5d66ebd547fa43af4c9121195572fba48556a6b7d6a4f0ddd10cf77d87d935482dbba4ec6ccc0b7f97a0521c83d6bf949621a2d32211b4b71a3c0c9c62814461bedc89038a34b500b4453b6c3ba7b6077882c5f850afdb189212a10a74a2815e44803949b1577510c064c5d110ca128aeca7a884bc6a12aa0c1e1de2d5eba1a913376de8dca58ac1b284c6315e9f4ebd3c5a7217ce849e34ad4a4d1bcc7809d5531c9d5f6d09f4752488e107a041c056e119728c1f450ba2f4d52d8af911f181735e57fe0dc45e8d8e7d091b2bc05c92025b967d46479319c3d9b6772e1c484b69e0ab5019a8f86c09091b5ae6bf85b72e728c1fc756d6c364d27704159021b9da665d1f53713eb9eb67ce8f165a3c6d04ebf8c6d28ea1f7bbdd508cdff1c8380303fb0be9281ccb77ad084a216242eabcb1b064456537405a7e330b1815353d544f6353e1b3acb869dc11989fe474c609c98fa08eb1c71d3b545e8837d4035e6316dc44d06c3fd717ee872e010e41b674c621b4a85a5ee504d0a83975c3453d28d8c0f0d7e0ca65a368d6cbdbf06da3aeaf9e70a268e7a010c8ce8a782f792982f00345e00e43ff74cf5ff77716bfec3d3e0ba0071e3cfc447f0651315191beb78c64a860bea6c207e283a127315cc2f238c52a80c928a41a81496d1e7c9bc5998f723a384cdd83a401b98766ba3fdb7c50c05a04da448f4bb5ca9055aadd6fd0cc6f08a73ef5562ce8cca20be347f5641b6843b04d7cd0bbb165f2130410b7bdc6902108dd9bd9c4e19813bf0cadb0a358702f87fadcac8ee904641ca67af9bd8a39c13b48ef31a2f014f1ef4fe7071cfa74298f9397f148f6567eb86b49c6e8f69ce06e72fd2a70b143e4d47ad9e5ca91807bf876d2007ca1efd6fbb4fd280b77a63b48a8dc6c40a19b2c5eb315d88ca50eb52ef870f0e3d5caba6a6c8e7b040d3418fb18df3bd546e002fe50a778bdd3ab164f4966f0e571c40d01a9ff490fa21ea481b3e45f0ff88b8fae7568dd196bd34edb64d938458bf3fec0d38cb182a85120bc41687a63e5768dbfe0041b1cababe1fe3da48241a6415b01c4b6e0f87624a0f5f767136ea4d84bdd4ed4e0d8dd6877e2890a18fe0d52fbdb99d82f2042ac5ad35378e426d6ca261d19fa4128a909de414bddc200f18c4bcd3545df7daac5bffabf4c2262b6d296f94906ef6a209c43861366270220bcb8d4ba2d9701f7264ecaa03aa8742a99306b4bf0aa38d1cb29a0a09f6b6ffbc29bfa0c882df5f521d6808b9088a23028542667700271b256f4787ff4b96b4fbfc4daefa17c3d0cc569dcdd414c5af3e40f23da66a8b5a9556cb0811338855510754672df0e1574c07028ace29e8dbc4eaeb1a1b02dbaba7006e985e14a9c0302e17907553064759146e2e32b8102ac652ff79073bad19ca780d7e7033b1459a0b90a4a7a8920872f02f89f4c68c4678e3dd6c72c9e2d0421a94b8d3027c08b79731e13f3db1512dfc4dcf92b6d67f169f8f0407f31225146753541dfea8013cbcc0158d3525e6e992ce95b7cb98ae28ea7de8d4af6745a89a02540f74792d3ee3e79a9a8e3d9713d595f2bb9d940a88362350a7a4937c4f91b1da21e12e6bc8e1707074720a043a07adc1b9bc05eddd9fd42efac6f2bea9c3280cc1dac049044312b76cf1ed20fd694b1940fe028a7a8466aad463b802c00377467c77baacd0c8a40ac9df750a698cbbd0e5bede8428c488b614cfed40c137398cba3b93a37ab1d2ece639eabc615d34786b688c8ff55f3f8f08c862c4d995e33f47a3d2bc962749030f700d7f193c4a8c24879bdea3c4b460b35e5df813c62a481c8612ec8e43d0d665fac3bcc6775e421241d5c6e7cdef7bb768ba69540d9346c5350bc5a02092a0d9a7ffa68452e2087fd2525322cfe6870495a5004c972941b6761efebc2d635413955373a522ddfce9bbb920160996d9d0b73ea64d6e08fb8ec6da0f4bd013096aa30d294e81f109aad163dde98a5237754eb74e32955e45d3cdb55bdd6eb4091b1d1f9dee6a59a2ff58d07fbb9cc9b39ef4dc2a0c4910b1abb0db93308c71566767d8295a5b46cbb19b3b5ebad1ad077ac5a320825beb8b98714b3df93c9e4acdefd8cbdb512e42b4d45a7c73c706f0639ec21e74550055295bd444abb87388c8b6b28f6fb4cbfd46a623dab2250596cfa7478369acf7ce5661f4817eaa41f4186cea3f85394b8a67b990be62aabae005c008692b236b09378ea313bd3f39b0271f1c0d8e00d9afa2e1cc176de734e0a144e41f5f047430ab2de4549a3e8bff69afb002b420460dbcf19340f62f18a8876c26819c68ef59909d20dcfe0d1ce5a56117048841e4ac794667380688a2e7e9a3836842838d8c11cb8466c7fc9e617993f46031e9c722ccb4fa7e31aff4d8cce6c133a733010da67fc792c8ddefb00f0e8d219963a152d34b7fda95eb2c6054204a42a4836a73b1bf4fbd8d6a1e8dec28035f4abcd435266a329cd99cfa50816a0611095b7895c93ec3337ee7f60e5ed42217a3962f719ad105ab96ad046af0e8dad3625f96df4f9bb199a627c6414b9960629521c72caf808349b6688767819431a13ed830caa883e8cf1f0c22d6b51bd712136b11f1fbf0de8b61c3c58d638dc36196ad061779596bfaf1bd016dc32c7d7c713378822eee0a25b0ba37844351ce6934c2d8a2c7d5c044ed246adf07eb6b8c8e937be9cb5eea6ff5cc4afe8ed208220e2e47ce70f33d1800cf433834a84634f3cb8915cc88e14aa7f449b92e177ee1ca90847da8d850a371f6accccb09dd95c671a121e5b5190b55f4aabc8ca3f6a4b97716ba3a9980bea418bd1422a010ccf2c4f95d8afe9dd9e776d7dd7577527ef5bbefd2e7e76f99d880963e6a60d77b2a075f86e80b26552e665b47c495dc62c72d20cf41593cff74854893a1f06c93828f5a726bc0bd93d1fb0c8a81bd6c4ff5bd7a816db0100cc7a149b54e7cc18543f3559d5e4ebe6cf7bf69fe39f3f68c8ba7168f94ea6666c5b8529b7a4d82d5cd43f92d8a49cb62ffe37bf6a4cd0c175ae909c99bcfabd96bc62b8bd79fa7dd83d05bbca7138fecec2d94772fda2f087cef80a2bdc10ebcb233fe410c05872a453379924a83dec583425ab799c23c9363d014c907ee6bd12964096a3c4b2f5cf234c3916355e578f99a50c8036337c510152225763bc1435fd74281c5902fc0b5b1c6c367b4f381d012d3bf7c81fa435a23939f7cc1f8c9a8d9292b719e08f18754d44a06ccd34640aad2d8a3d19a53da410cdcc0bd7fd2810da5a167bf0755cf40f86a0eb2480bd259ba2c3721636a219103b912058f0127fa60ef19420589b28260d9f8dae09c9ae9050fe6596e390f9a2b0104908d82f9ffce0fb8f1854c068f7ccd3c4f95dd21bd23e33a4ce7eebf8b3d5315623478a4b1d548b5c526a3ea7393ac0f4558d3da3808832312943ea7761c09a57725e57ebfb1cf628f3e300ef362cb3c69f5d29c512bcd46bb0b3499750ab85863c22b983327991920fd2075593092d3af384f006a42dc722a74d488636563b392f12b9419c4730b6883d8147f91162c125b49ce966d2db0064189ef4e94c8367a0f23a7cba39ebcda86e8564cd9bddc454beb9157d2c8984fda42aca9880658c860d8ecf20dd3fa6894642b86744d0ad259f8204646df50b08d904f85527c812078c7b9a0fe5b7c41f3c2d3081eb8f6b1774e0da71dcb5cd4b660295ea750258fb15db4b9a1779b17caba3f58af683e24c34ed131f99c1160c5eae5c66994ec38d0f6ebe5af524c4a20357eb61cea0d3538a5c78707a3d065d6164903632c831b6ec18d84f2d7109daa0bdc79e64e1fc1ef046556de2d27ebbf76f16ad73456d4245990fd208948254390ca88d5a01b5a2091cc6a7758c6b41a52ef0b7e87b17f97d4a1d69844de16c0583adcd11a327e7e2963990d711de0ffbbba94bd003a7d2d599438a121926bc517c411024092ef48c216493de957bc9d05d345bcf76265e22de95a759303d0915c2bbb5ae0f9ebe87a043d195bf1d6040d2ada553703c3b229142c16821ad50871a68a83438a02ff1d04af13c24e3a0378dc41835b206453dbcd576d2215784062ff6462ac3cf31e13f61e42085feeaa855ad60329bb73d6bd70108518dc46d9bc3e12a990210004267d8cf25d13a1bca0aedc603a356f58cde0d2ce2cb155d75d582d2057ceba20c699c0241739fc4866befbe56c17eba78e81c98c7aac44266013fc59367489514d58cc2237ef7a2194ef535a2bf0ad00cc6375cc01ccac1910c8abf353f92302fa3a7239c2f41a90aa2d26b05b6f466ea685594caf864c249f2e6eef29aa4d73854d12d4570a7fb5d6a7bfaaf9628c232b3beaafa99bf46a8e130c41515f73655cecb84db7732372ea37a1d43d15ad02",
"patch": {
"balances": {
"balances": [
[
"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
1125899906842624
],
[
"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
1125899906842624
],
[
"5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY",
1125899906842624
],
[
"5HpG9w8EBLe5XCrbczpwq5TSXvedjrBGCwqxK1iQ7qUsSWFc",
1125899906842624
]
]
},
"keccakDifficultyAdjustment": {
"initialDifficulty": [
0,
9,
61,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
]
},
"md5DifficultyAdjustment": {
"initialDifficulty": [
0,
9,
61,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
]
},
"sha3DifficultyAdjustment": {
"initialDifficulty": [
0,
9,
61,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
]
},
"utxo": {
"genesisUtxos": [
[
1125899906842624,
"0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
],
[
1125899906842624,
"0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48"
],
[
1125899906842624,
"0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f"
],
[
1125899906842624,
"0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e"
]
]
}
}
}
}
}
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..57ce217
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,35 @@
+root = true
+[*]
+indent_style=tab
+indent_size=tab
+tab_width=4
+end_of_line=lf
+charset=utf-8
+trim_trailing_whitespace=true
+max_line_length=100
+insert_final_newline=true
+
+[*.yml]
+indent_style=space
+indent_size=2
+tab_width=8
+end_of_line=lf
+
+[*.sh]
+indent_style=space
+indent_size=2
+tab_width=8
+end_of_line=lf
+
+[*.js]
+indent_style=space
+indent_size=4
+
+
+[*.jsx]
+indent_style=space
+indent_size=4
+
+[Dockerfile]
+indent_style=space
+indent_size=2
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ab80259
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+**/*.rs.bk
+**/target/
+addresses.json
+
+validators
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..6d62aa5
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,10605 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[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 = "academy-pow"
+version = "3.0.0"
+dependencies = [
+ "academy-pow-runtime",
+ "clap",
+ "futures",
+ "hex",
+ "jsonrpsee",
+ "log",
+ "multi-pow",
+ "pallet-transaction-payment-rpc",
+ "parity-scale-codec",
+ "rand",
+ "sc-basic-authorship",
+ "sc-chain-spec",
+ "sc-cli",
+ "sc-client-api",
+ "sc-consensus",
+ "sc-consensus-manual-seal",
+ "sc-consensus-pow",
+ "sc-executor",
+ "sc-network",
+ "sc-rpc-api",
+ "sc-service",
+ "sc-telemetry",
+ "sc-transaction-pool",
+ "sc-transaction-pool-api",
+ "serde",
+ "serde_json",
+ "sha3",
+ "sp-api",
+ "sp-block-builder",
+ "sp-blockchain",
+ "sp-consensus",
+ "sp-consensus-pow",
+ "sp-core",
+ "sp-inherents",
+ "sp-io",
+ "sp-keyring",
+ "sp-runtime",
+ "sp-timestamp",
+ "sp-transaction-pool",
+ "substrate-build-script-utils",
+ "substrate-frame-rpc-system",
+]
+
+[[package]]
+name = "academy-pow-runtime"
+version = "3.0.0"
+dependencies = [
+ "async-trait",
+ "frame-executive",
+ "frame-support",
+ "frame-system",
+ "frame-system-rpc-runtime-api",
+ "hex-literal",
+ "log",
+ "multi-pow",
+ "pallet-balances",
+ "pallet-timestamp",
+ "pallet-transaction-payment",
+ "pallet-transaction-payment-rpc-runtime-api",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-api",
+ "sp-block-builder",
+ "sp-consensus-pow",
+ "sp-core",
+ "sp-genesis-builder",
+ "sp-inherents",
+ "sp-io",
+ "sp-keystore",
+ "sp-offchain",
+ "sp-runtime",
+ "sp-session",
+ "sp-std",
+ "sp-transaction-pool",
+ "sp-version",
+ "substrate-wasm-builder",
+]
+
+[[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.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
+dependencies = [
+ "gimli 0.31.1",
+]
+
+[[package]]
+name = "adler2"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+
+[[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.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
+dependencies = [
+ "cfg-if",
+ "cipher 0.4.4",
+ "cpufeatures",
+]
+
+[[package]]
+name = "aes-gcm"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
+dependencies = [
+ "aead",
+ "aes",
+ "cipher 0.4.4",
+ "ctr",
+ "ghash",
+ "subtle 2.6.1",
+]
+
+[[package]]
+name = "ahash"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
+dependencies = [
+ "cfg-if",
+ "getrandom",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "allocator-api2"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
+
+[[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"
+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 = "anstream"
+version = "0.6.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
+dependencies = [
+ "anstyle",
+ "once_cell",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
+
+[[package]]
+name = "approx"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "aquamarine"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21cc1548309245035eb18aa7f0967da6bc65587005170c56e6ef2788a4cf3f4e"
+dependencies = [
+ "include_dir",
+ "itertools 0.10.5",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "ark-bls12-377"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb00293ba84f51ce3bd026bd0de55899c4e68f0a39a5728cebae3a73ffdc0a4f"
+dependencies = [
+ "ark-ec",
+ "ark-ff",
+ "ark-std",
+]
+
+[[package]]
+name = "ark-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 0.10.5",
+ "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 0.10.5",
+ "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 = "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 = "array-bytes"
+version = "6.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d5dde061bd34119e902bbb2d9b90c5692635cf59fb91d582c2b68043f1b8293"
+
+[[package]]
+name = "arrayref"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
+
+[[package]]
+name = "arrayvec"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
+
+[[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 0.1.0",
+ "displaydoc",
+ "nom",
+ "num-traits",
+ "rusticata-macros",
+ "thiserror",
+ "time",
+]
+
+[[package]]
+name = "asn1-rs"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048"
+dependencies = [
+ "asn1-rs-derive 0.5.1",
+ "asn1-rs-impl 0.2.0",
+ "displaydoc",
+ "nom",
+ "num-traits",
+ "rusticata-macros",
+ "thiserror",
+ "time",
+]
+
+[[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 0.12.6",
+]
+
+[[package]]
+name = "asn1-rs-derive"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+ "synstructure 0.13.1",
+]
+
+[[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-rs-impl"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "assert_matches"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
+
+[[package]]
+name = "async-channel"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
+dependencies = [
+ "concurrent-queue",
+ "event-listener 2.5.3",
+ "futures-core",
+]
+
+[[package]]
+name = "async-io"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059"
+dependencies = [
+ "async-lock",
+ "cfg-if",
+ "concurrent-queue",
+ "futures-io",
+ "futures-lite",
+ "parking",
+ "polling",
+ "rustix 0.38.43",
+ "slab",
+ "tracing",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "async-lock"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
+dependencies = [
+ "event-listener 5.4.0",
+ "event-listener-strategy",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-trait"
+version = "0.1.85"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "asynchronous-codec"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4057f2c32adbb2fc158e22fb38433c8e9bbf76b75a4732c7c0cbaf695fb65568"
+dependencies = [
+ "bytes",
+ "futures-sink",
+ "futures-util",
+ "memchr",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
+name = "attohttpc"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2"
+dependencies = [
+ "http 0.2.12",
+ "log",
+ "url",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
+
+[[package]]
+name = "backtrace"
+version = "0.3.74"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
+dependencies = [
+ "addr2line 0.24.2",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object 0.36.7",
+ "rustc-demangle",
+ "windows-targets 0.52.6",
+]
+
+[[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.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
+
+[[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.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[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 = "bitcoin-internals"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb"
+
+[[package]]
+name = "bitcoin_hashes"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b"
+dependencies = [
+ "bitcoin-internals",
+ "hex-conservative",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
+
+[[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.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330"
+dependencies = [
+ "byte-tools",
+ "crypto-mac 0.7.0",
+ "digest 0.8.1",
+ "opaque-debug 0.2.3",
+]
+
+[[package]]
+name = "blake2"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
+dependencies = [
+ "digest 0.10.7",
+]
+
+[[package]]
+name = "blake2b_simd"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "constant_time_eq",
+]
+
+[[package]]
+name = "blake2s_simd"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94230421e395b9920d23df13ea5d77a20e1725331f90fbbf6df6040b33f756ae"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "constant_time_eq",
+]
+
+[[package]]
+name = "blake3"
+version = "1.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "cc",
+ "cfg-if",
+ "constant_time_eq",
+]
+
+[[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 = "bounded-collections"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d077619e9c237a5d1875166f5e8033e8f6bff0c96f8caf81e1c2d7738c431bf"
+dependencies = [
+ "log",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+]
+
+[[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.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "build-helper"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdce191bf3fa4995ce948c8c83b4640a1745457a149e73c6db75b4ffe36aad5f"
+dependencies = [
+ "semver 0.6.0",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
+
+[[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 = "bytemuck"
+version = "1.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
+
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
+[[package]]
+name = "bytes"
+version = "1.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
+
+[[package]]
+name = "c2-chacha"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d27dae93fe7b1e0424dc57179ac396908c26b035a87234809f5c4dfd1b47dc80"
+dependencies = [
+ "cipher 0.2.5",
+ "ppv-lite86",
+]
+
+[[package]]
+name = "camino"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cargo-platform"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "cargo_metadata"
+version = "0.15.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a"
+dependencies = [
+ "camino",
+ "cargo-platform",
+ "semver 1.0.24",
+ "serde",
+ "serde_json",
+ "thiserror",
+]
+
+[[package]]
+name = "cc"
+version = "1.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8293772165d9345bdaaa39b45b2109591e63fe5e6fbc23c6ff930a048aa310b"
+dependencies = [
+ "jobserver",
+ "libc",
+ "shlex",
+]
+
+[[package]]
+name = "cfg-expr"
+version = "0.15.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
+dependencies = [
+ "smallvec",
+]
+
+[[package]]
+name = "cfg-if"
+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 = "chacha"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddf3c081b5fba1e5615640aae998e0fbd10c24cbd897ee39ed754a77601a4862"
+dependencies = [
+ "byteorder",
+ "keystream",
+]
+
+[[package]]
+name = "chacha20"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
+dependencies = [
+ "cfg-if",
+ "cipher 0.4.4",
+ "cpufeatures",
+]
+
+[[package]]
+name = "chacha20poly1305"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
+dependencies = [
+ "aead",
+ "chacha20",
+ "cipher 0.4.4",
+ "poly1305",
+ "zeroize",
+]
+
+[[package]]
+name = "chrono"
+version = "0.4.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "cid"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9b68e3193982cd54187d71afdb2a271ad4cf8af157858e9cb911b91321de143"
+dependencies = [
+ "core2",
+ "multibase",
+ "multihash 0.17.0",
+ "serde",
+ "unsigned-varint 0.7.2",
+]
+
+[[package]]
+name = "cid"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd94671561e36e4e7de75f753f577edafb0e7c05d6e4547229fdf7938fbcd2c3"
+dependencies = [
+ "core2",
+ "multibase",
+ "multihash 0.18.1",
+ "serde",
+ "unsigned-varint 0.7.2",
+]
+
+[[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.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+dependencies = [
+ "crypto-common",
+ "inout",
+ "zeroize",
+]
+
+[[package]]
+name = "clap"
+version = "4.5.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+ "terminal_size",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c"
+dependencies = [
+ "heck 0.5.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+
+[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width 0.1.14",
+]
+
+[[package]]
+name = "colorchoice"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
+
+[[package]]
+name = "combine"
+version = "4.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[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.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "console"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b"
+dependencies = [
+ "encode_unicode",
+ "libc",
+ "once_cell",
+ "unicode-width 0.2.0",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "const-oid"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+
+[[package]]
+name = "const-random"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
+dependencies = [
+ "const-random-macro",
+]
+
+[[package]]
+name = "const-random-macro"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
+dependencies = [
+ "getrandom",
+ "once_cell",
+ "tiny-keccak",
+]
+
+[[package]]
+name = "constant_time_eq"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
+
+[[package]]
+name = "constcat"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd7e35aee659887cbfb97aaf227ac12cad1a9d7c71e55ff3376839ed4e282d08"
+
+[[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.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "cranelift-bforest"
+version = "0.95.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1277fbfa94bc82c8ec4af2ded3e639d49ca5f7f3c7eeab2c66accd135ece4e70"
+dependencies = [
+ "cranelift-entity",
+]
+
+[[package]]
+name = "cranelift-codegen"
+version = "0.95.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6e8c31ad3b2270e9aeec38723888fe1b0ace3bea2b06b3f749ccf46661d3220"
+dependencies = [
+ "bumpalo",
+ "cranelift-bforest",
+ "cranelift-codegen-meta",
+ "cranelift-codegen-shared",
+ "cranelift-entity",
+ "cranelift-isle",
+ "gimli 0.27.3",
+ "hashbrown 0.13.2",
+ "log",
+ "regalloc2 0.6.1",
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cranelift-codegen-meta"
+version = "0.95.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8ac5ac30d62b2d66f12651f6b606dbdfd9c2cfd0908de6b387560a277c5c9da"
+dependencies = [
+ "cranelift-codegen-shared",
+]
+
+[[package]]
+name = "cranelift-codegen-shared"
+version = "0.95.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd82b8b376247834b59ed9bdc0ddeb50f517452827d4a11bccf5937b213748b8"
+
+[[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.95.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64a25d9d0a0ae3079c463c34115ec59507b4707175454f0eee0891e83e30e82d"
+dependencies = [
+ "cranelift-codegen",
+ "log",
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cranelift-isle"
+version = "0.95.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80de6a7d0486e4acbd5f9f87ec49912bf4c8fb6aea00087b989685460d4469ba"
+
+[[package]]
+name = "cranelift-native"
+version = "0.95.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb6b03e0e03801c4b3fd8ce0758a94750c07a44e7944cc0ffbf0d3f2e7c79b00"
+dependencies = [
+ "cranelift-codegen",
+ "libc",
+ "target-lexicon",
+]
+
+[[package]]
+name = "cranelift-wasm"
+version = "0.95.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff3220489a3d928ad91e59dd7aeaa8b3de18afb554a6211213673a71c90737ac"
+dependencies = [
+ "cranelift-codegen",
+ "cranelift-entity",
+ "cranelift-frontend",
+ "itertools 0.10.5",
+ "log",
+ "smallvec",
+ "wasmparser",
+ "wasmtime-types",
+]
+
+[[package]]
+name = "crc"
+version = "3.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
+dependencies = [
+ "crc-catalog",
+]
+
+[[package]]
+name = "crc-catalog"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
+
+[[package]]
+name = "crc32fast"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[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.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
+dependencies = [
+ "generic-array 0.14.7",
+ "rand_core",
+ "subtle 2.6.1",
+ "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",
+ "rand_core",
+ "typenum",
+]
+
+[[package]]
+name = "crypto-mac"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
+dependencies = [
+ "generic-array 0.12.4",
+ "subtle 1.0.0",
+]
+
+[[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 2.6.1",
+]
+
+[[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 = "curve25519-dalek"
+version = "4.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "curve25519-dalek-derive",
+ "digest 0.10.7",
+ "fiat-crypto",
+ "rustc_version",
+ "subtle 2.6.1",
+ "zeroize",
+]
+
+[[package]]
+name = "curve25519-dalek-derive"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "cxx"
+version = "1.0.136"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad7c7515609502d316ab9a24f67dc045132d93bfd3f00713389e90d9898bf30d"
+dependencies = [
+ "cc",
+ "cxxbridge-cmd",
+ "cxxbridge-flags",
+ "cxxbridge-macro",
+ "foldhash",
+ "link-cplusplus",
+]
+
+[[package]]
+name = "cxx-build"
+version = "1.0.136"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8bfd16fca6fd420aebbd80d643c201ee4692114a0de208b790b9cd02ceae65fb"
+dependencies = [
+ "cc",
+ "codespan-reporting",
+ "proc-macro2",
+ "quote",
+ "scratch",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "cxxbridge-cmd"
+version = "1.0.136"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c33fd49f5d956a1b7ee5f7a9768d58580c6752838d92e39d0d56439efdedc35"
+dependencies = [
+ "clap",
+ "codespan-reporting",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "cxxbridge-flags"
+version = "1.0.136"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be0f1077278fac36299cce8446effd19fe93a95eedb10d39265f3bf67b3036c9"
+
+[[package]]
+name = "cxxbridge-macro"
+version = "1.0.136"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3da7e4d6e74af6b79031d264b2f13c3ea70af1978083741c41ffce9308f1f24f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "dashmap"
+version = "5.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
+dependencies = [
+ "cfg-if",
+ "hashbrown 0.14.5",
+ "lock_api",
+ "once_cell",
+ "parking_lot_core 0.9.10",
+]
+
+[[package]]
+name = "data-encoding"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f"
+
+[[package]]
+name = "data-encoding-macro"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b16d9d0d88a5273d830dac8b78ceb217ffc9b1d5404e5597a3542515329405b"
+dependencies = [
+ "data-encoding",
+ "data-encoding-macro-internal",
+]
+
+[[package]]
+name = "data-encoding-macro-internal"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1145d32e826a7748b69ee8fc62d3e6355ff7f1051df53141e7048162fc90481b"
+dependencies = [
+ "data-encoding",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "der"
+version = "0.7.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
+dependencies = [
+ "const-oid",
+ "zeroize",
+]
+
+[[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 = "der-parser"
+version = "9.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553"
+dependencies = [
+ "asn1-rs 0.6.2",
+ "displaydoc",
+ "nom",
+ "num-bigint",
+ "num-traits",
+ "rusticata-macros",
+]
+
+[[package]]
+name = "deranged"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
+dependencies = [
+ "powerfmt",
+]
+
+[[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.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "derive_more"
+version = "0.99.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce"
+dependencies = [
+ "convert_case",
+ "proc-macro2",
+ "quote",
+ "rustc_version",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "derive_more"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05"
+dependencies = [
+ "derive_more-impl",
+]
+
+[[package]]
+name = "derive_more-impl"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[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"
+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.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer 0.10.4",
+ "const-oid",
+ "crypto-common",
+ "subtle 2.6.1",
+]
+
+[[package]]
+name = "directories"
+version = "5.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35"
+dependencies = [
+ "dirs-sys",
+]
+
+[[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"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys 0.48.0",
+]
+
+[[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 = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "docify"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a772b62b1837c8f060432ddcc10b17aae1453ef17617a99bc07789252d2a5896"
+dependencies = [
+ "docify_macros",
+]
+
+[[package]]
+name = "docify_macros"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60e6be249b0a462a14784a99b19bf35a667bb5e09de611738bb7362fa4c95ff7"
+dependencies = [
+ "common-path",
+ "derive-syn-parse",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "syn 2.0.96",
+ "termcolor",
+ "toml 0.8.19",
+ "walkdir",
+]
+
+[[package]]
+name = "downcast"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1"
+
+[[package]]
+name = "dtoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653"
+
+[[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.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
+
+[[package]]
+name = "ecdsa"
+version = "0.16.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
+dependencies = [
+ "der",
+ "digest 0.10.7",
+ "elliptic-curve",
+ "rfc6979",
+ "serdect",
+ "signature",
+ "spki",
+]
+
+[[package]]
+name = "ed25519"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+dependencies = [
+ "pkcs8",
+ "signature",
+]
+
+[[package]]
+name = "ed25519-dalek"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "rand_core",
+ "serde",
+ "sha2 0.10.8",
+ "subtle 2.6.1",
+ "zeroize",
+]
+
+[[package]]
+name = "ed25519-zebra"
+version = "4.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d9ce6874da5d4415896cd45ffbc4d1cfc0c4f9c079427bd870742c30f2f65a9"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "hashbrown 0.14.5",
+ "hex",
+ "rand_core",
+ "sha2 0.10.8",
+ "zeroize",
+]
+
+[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
+[[package]]
+name = "elliptic-curve"
+version = "0.13.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
+dependencies = [
+ "base16ct",
+ "crypto-bigint",
+ "digest 0.10.7",
+ "ff",
+ "generic-array 0.14.7",
+ "group",
+ "pkcs8",
+ "rand_core",
+ "sec1",
+ "serdect",
+ "subtle 2.6.1",
+ "zeroize",
+]
+
+[[package]]
+name = "encode_unicode"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
+
+[[package]]
+name = "enum-as-inner"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116"
+dependencies = [
+ "heck 0.4.1",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "enum-as-inner"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc"
+dependencies = [
+ "heck 0.5.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "env_logger"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580"
+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 = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
+[[package]]
+name = "errno"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
+dependencies = [
+ "libc",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "event-listener"
+version = "2.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
+
+[[package]]
+name = "event-listener"
+version = "5.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae"
+dependencies = [
+ "concurrent-queue",
+ "parking",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "event-listener-strategy"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2"
+dependencies = [
+ "event-listener 5.4.0",
+ "pin-project-lite",
+]
+
+[[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 = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2c470c71d91ecbd179935b24170459e926382eaaa86b590b78814e180d8a8e2"
+dependencies = [
+ "blake2 0.10.6",
+ "file-guard",
+ "fs-err",
+ "prettyplease 0.2.29",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "fallible-iterator"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
+
+[[package]]
+name = "fallible-iterator"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
+
+[[package]]
+name = "fastrand"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+
+[[package]]
+name = "fdlimit"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e182f7dbc2ef73d9ef67351c5fbbea084729c48362d3ce9dd44c28e32e277fe5"
+dependencies = [
+ "libc",
+ "thiserror",
+]
+
+[[package]]
+name = "ff"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449"
+dependencies = [
+ "rand_core",
+ "subtle 2.6.1",
+]
+
+[[package]]
+name = "fiat-crypto"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
+
+[[package]]
+name = "file-guard"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21ef72acf95ec3d7dbf61275be556299490a245f017cf084bd23b4f68cf9407c"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[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 = "filetime"
+version = "0.2.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "libredox",
+ "windows-sys 0.59.0",
+]
+
+[[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.3",
+ "scale-info",
+]
+
+[[package]]
+name = "fixed-hash"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534"
+dependencies = [
+ "byteorder",
+ "rand",
+ "rustc-hex",
+ "static_assertions",
+]
+
+[[package]]
+name = "fixedbitset"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+
+[[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 = "foldhash"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
+
+[[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 = "13.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6736bef9fd175fafbb97495565456651c43ccac2ae550faee709e11534e3621"
+dependencies = [
+ "parity-scale-codec",
+]
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "forwarded-header-value"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9"
+dependencies = [
+ "nonempty",
+ "thiserror",
+]
+
+[[package]]
+name = "fragile"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa"
+
+[[package]]
+name = "frame-benchmarking"
+version = "37.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48554572bd164ee905a6ff3378e46c2101610fd2ffe3110875a6503a240fb3d7"
+dependencies = [
+ "frame-support",
+ "frame-support-procedural",
+ "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-storage",
+ "static_assertions",
+]
+
+[[package]]
+name = "frame-executive"
+version = "37.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dfc9b1cdc305826ef1196675a53ef7f2db8967a6cf5632775119c41d6f4e299"
+dependencies = [
+ "aquamarine",
+ "frame-support",
+ "frame-system",
+ "frame-try-runtime",
+ "log",
+ "parity-scale-codec",
+ "scale-info",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-tracing",
+]
+
+[[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 = "37.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "847939177e3637c1ec2f78eecf0755910763b8d6c3dfc04aea2efec33823b3af"
+dependencies = [
+ "aquamarine",
+ "array-bytes",
+ "bitflags 1.3.2",
+ "docify",
+ "environmental",
+ "frame-metadata",
+ "frame-support-procedural",
+ "impl-trait-for-tuples",
+ "k256",
+ "log",
+ "macro_magic",
+ "parity-scale-codec",
+ "paste",
+ "scale-info",
+ "serde",
+ "serde_json",
+ "smallvec",
+ "sp-api",
+ "sp-arithmetic",
+ "sp-core",
+ "sp-crypto-hashing-proc-macro",
+ "sp-debug-derive",
+ "sp-genesis-builder",
+ "sp-inherents",
+ "sp-io",
+ "sp-metadata-ir",
+ "sp-runtime",
+ "sp-staking",
+ "sp-state-machine",
+ "sp-std",
+ "sp-tracing",
+ "sp-weights",
+ "static_assertions",
+ "tt-call",
+]
+
+[[package]]
+name = "frame-support-procedural"
+version = "30.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e8f9b6bc1517a6fcbf0b2377e5c8c6d39f5bb7862b191a59a9992081d63972d"
+dependencies = [
+ "Inflector",
+ "cfg-expr",
+ "derive-syn-parse",
+ "expander",
+ "frame-support-procedural-tools",
+ "itertools 0.11.0",
+ "macro_magic",
+ "proc-macro-warning 1.0.2",
+ "proc-macro2",
+ "quote",
+ "sp-crypto-hashing",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "frame-support-procedural-tools"
+version = "13.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81a088fd6fda5f53ff0c17fc7551ce8bd0ead14ba742228443c8196296a7369b"
+dependencies = [
+ "frame-support-procedural-tools-derive",
+ "proc-macro-crate 3.2.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "frame-support-procedural-tools-derive"
+version = "12.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed971c6435503a099bdac99fe4c5bea08981709e5b5a0a8535a1856f48561191"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "frame-system"
+version = "37.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "043790fff021477061b207fd6b33743793b63fc64a583358956787229d039717"
+dependencies = [
+ "cfg-if",
+ "docify",
+ "frame-support",
+ "log",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+ "sp-std",
+ "sp-version",
+ "sp-weights",
+]
+
+[[package]]
+name = "frame-system-rpc-runtime-api"
+version = "34.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "475c4f8604ba7e4f05cd2c881ba71105093e638b9591ec71a8db14a64b3b4ec3"
+dependencies = [
+ "docify",
+ "parity-scale-codec",
+ "sp-api",
+]
+
+[[package]]
+name = "frame-try-runtime"
+version = "0.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec60be1ddc39bd312496e58b2dd82e5f3d1e0609b69f9586ba6967a36453e495"
+dependencies = [
+ "frame-support",
+ "parity-scale-codec",
+ "sp-api",
+ "sp-runtime",
+]
+
+[[package]]
+name = "fs-err"
+version = "2.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41"
+dependencies = [
+ "autocfg",
+]
+
+[[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.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-bounded"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b07bbbe7d7e78809544c6f718d875627addc73a7c3582447abc052cd3dc67e0"
+dependencies = [
+ "futures-timer",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+ "num_cpus",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+
+[[package]]
+name = "futures-lite"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "futures-macro"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "futures-rustls"
+version = "0.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35bd3cf68c183738046838e300353e4716c674dc5e56890de4826801a6622a28"
+dependencies = [
+ "futures-io",
+ "rustls 0.21.12",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
+
+[[package]]
+name = "futures-task"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-timer"
+version = "3.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
+
+[[package]]
+name = "futures-util"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
+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",
+ "zeroize",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "getrandom_or_panic"
+version = "0.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ea1015b5a70616b688dc230cfe50c8af89d972cb132d5a622814d29773b10b9"
+dependencies = [
+ "rand",
+ "rand_core",
+]
+
+[[package]]
+name = "ghash"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
+dependencies = [
+ "opaque-debug 0.3.1",
+ "polyval",
+]
+
+[[package]]
+name = "gimli"
+version = "0.27.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
+dependencies = [
+ "fallible-iterator 0.2.0",
+ "indexmap 1.9.3",
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "gimli"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
+dependencies = [
+ "fallible-iterator 0.3.0",
+ "stable_deref_trait",
+]
+
+[[package]]
+name = "gimli"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
+
+[[package]]
+name = "governor"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68a7f542ee6b35af73b06abc0dad1c1bae89964e4e253bc4b587b91c9637867b"
+dependencies = [
+ "cfg-if",
+ "dashmap",
+ "futures",
+ "futures-timer",
+ "no-std-compat",
+ "nonzero_ext",
+ "parking_lot 0.12.3",
+ "portable-atomic",
+ "quanta",
+ "rand",
+ "smallvec",
+ "spinning_top",
+]
+
+[[package]]
+name = "group"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
+dependencies = [
+ "ff",
+ "rand_core",
+ "subtle 2.6.1",
+]
+
+[[package]]
+name = "h2"
+version = "0.3.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http 0.2.12",
+ "indexmap 2.7.0",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "h2"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http 1.2.0",
+ "indexmap 2.7.0",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "hash-db"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e7d7786361d7425ae2fe4f9e407eb0efaa0840f5212d109cc018c40c35c6ab4"
+
+[[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"
+
+[[package]]
+name = "hashbrown"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
+dependencies = [
+ "ahash",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.14.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+dependencies = [
+ "ahash",
+ "allocator-api2",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
+dependencies = [
+ "allocator-api2",
+ "equivalent",
+ "foldhash",
+]
+
+[[package]]
+name = "hashlink"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
+dependencies = [
+ "hashbrown 0.14.5",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "hermit-abi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+
+[[package]]
+name = "hermit-abi"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
+
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
+[[package]]
+name = "hex-conservative"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "212ab92002354b4819390025006c897e8140934349e8635c9b077f47b4dcbd20"
+
+[[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.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
+dependencies = [
+ "hmac 0.12.1",
+]
+
+[[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.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
+dependencies = [
+ "digest 0.10.7",
+]
+
+[[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 = "home"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[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.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
+dependencies = [
+ "bytes",
+ "http 0.2.12",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
+dependencies = [
+ "bytes",
+ "http 1.2.0",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[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.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "h2 0.3.26",
+ "http 0.2.12",
+ "http-body 0.4.6",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "socket2 0.5.8",
+ "tokio",
+ "tower-service",
+ "tracing",
+ "want",
+]
+
+[[package]]
+name = "hyper"
+version = "1.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "h2 0.4.7",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "hyper 1.5.2",
+ "pin-project-lite",
+ "tokio",
+ "tower-service",
+]
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.61"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core 0.52.0",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "icu_collections"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid_transform"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
+dependencies = [
+ "displaydoc",
+ "icu_locid",
+ "icu_locid_transform_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid_transform_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
+
+[[package]]
+name = "icu_normalizer"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "utf16_iter",
+ "utf8_iter",
+ "write16",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
+
+[[package]]
+name = "icu_properties"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_locid_transform",
+ "icu_properties_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
+
+[[package]]
+name = "icu_provider"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
+dependencies = [
+ "displaydoc",
+ "icu_locid",
+ "icu_provider_macros",
+ "stable_deref_trait",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_provider_macros"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "idna"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "if-addrs"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cabb0019d51a643781ff15c9c8a3e5dedc365c47211270f4e8f82812fedd8f0a"
+dependencies = [
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "if-watch"
+version = "3.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdf9d64cfcf380606e64f9a0bcf493616b65331199f984151a6fa11a7b3cde38"
+dependencies = [
+ "async-io",
+ "core-foundation",
+ "fnv",
+ "futures",
+ "if-addrs",
+ "ipnet",
+ "log",
+ "netlink-packet-core",
+ "netlink-packet-route",
+ "netlink-proto",
+ "netlink-sys",
+ "rtnetlink",
+ "system-configuration",
+ "tokio",
+ "windows",
+]
+
+[[package]]
+name = "igd-next"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "064d90fec10d541084e7b39ead8875a5a80d9114a2b18791565253bae25f49e4"
+dependencies = [
+ "async-trait",
+ "attohttpc",
+ "bytes",
+ "futures",
+ "http 0.2.12",
+ "hyper 0.14.32",
+ "log",
+ "rand",
+ "tokio",
+ "url",
+ "xmltree",
+]
+
+[[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.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "include_dir"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd"
+dependencies = [
+ "include_dir_macros",
+]
+
+[[package]]
+name = "include_dir_macros"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
+dependencies = [
+ "proc-macro2",
+ "quote",
+]
+
+[[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",
+ "serde",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.15.2",
+]
+
+[[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.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
+dependencies = [
+ "cfg-if",
+]
+
+[[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.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
+dependencies = [
+ "hermit-abi 0.3.9",
+ "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.8",
+ "widestring",
+ "windows-sys 0.48.0",
+ "winreg",
+]
+
+[[package]]
+name = "ipnet"
+version = "2.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708"
+
+[[package]]
+name = "is-terminal"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
+dependencies = [
+ "hermit-abi 0.4.0",
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
+
+[[package]]
+name = "itertools"
+version = "0.10.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itertools"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
+
+[[package]]
+name = "jobserver"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "jsonrpsee"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b089779ad7f80768693755a031cc14a7766aba707cbe886674e3f79e9b7e47"
+dependencies = [
+ "jsonrpsee-core",
+ "jsonrpsee-proc-macros",
+ "jsonrpsee-server",
+ "jsonrpsee-types",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "jsonrpsee-core"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79712302e737d23ca0daa178e752c9334846b08321d439fd89af9a384f8c830b"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "beef",
+ "bytes",
+ "futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "jsonrpsee-types",
+ "parking_lot 0.12.3",
+ "rand",
+ "rustc-hash",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "jsonrpsee-proc-macros"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7895f186d5921065d96e16bd795e5ca89ac8356ec423fafc6e3d7cf8ec11aee4"
+dependencies = [
+ "heck 0.5.0",
+ "proc-macro-crate 3.2.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "jsonrpsee-server"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "654afab2e92e5d88ebd8a39d6074483f3f2bfdf91c5ac57fe285e7127cdd4f51"
+dependencies = [
+ "anyhow",
+ "futures-util",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "hyper 1.5.2",
+ "hyper-util",
+ "jsonrpsee-core",
+ "jsonrpsee-types",
+ "pin-project",
+ "route-recognizer",
+ "serde",
+ "serde_json",
+ "soketto",
+ "thiserror",
+ "tokio",
+ "tokio-stream",
+ "tokio-util",
+ "tower",
+ "tracing",
+]
+
+[[package]]
+name = "jsonrpsee-types"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c465fbe385238e861fdc4d1c85e04ada6c1fd246161d26385c1b311724d2af"
+dependencies = [
+ "beef",
+ "http 1.2.0",
+ "serde",
+ "serde_json",
+ "thiserror",
+]
+
+[[package]]
+name = "k256"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b"
+dependencies = [
+ "cfg-if",
+ "ecdsa",
+ "elliptic-curve",
+ "once_cell",
+ "serdect",
+ "sha2 0.10.8",
+]
+
+[[package]]
+name = "keccak"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654"
+dependencies = [
+ "cpufeatures",
+]
+
+[[package]]
+name = "keystream"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c33070833c9ee02266356de0c43f723152bd38bd96ddf52c82b3af10c9138b28"
+
+[[package]]
+name = "kvdb"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7d770dcb02bf6835887c3a979b5107a04ff4bbde97a5f0928d27404a155add9"
+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.3",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "libc"
+version = "0.2.169"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
+
+[[package]]
+name = "libm"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
+
+[[package]]
+name = "libp2p"
+version = "0.52.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e94495eb319a85b70a68b85e2389a95bb3555c71c49025b78c691a854a7e6464"
+dependencies = [
+ "bytes",
+ "either",
+ "futures",
+ "futures-timer",
+ "getrandom",
+ "instant",
+ "libp2p-allow-block-list",
+ "libp2p-connection-limits",
+ "libp2p-core",
+ "libp2p-dns",
+ "libp2p-identify",
+ "libp2p-identity",
+ "libp2p-kad",
+ "libp2p-mdns",
+ "libp2p-metrics",
+ "libp2p-noise",
+ "libp2p-ping",
+ "libp2p-quic",
+ "libp2p-request-response",
+ "libp2p-swarm",
+ "libp2p-tcp",
+ "libp2p-upnp",
+ "libp2p-wasm-ext",
+ "libp2p-websocket",
+ "libp2p-yamux",
+ "multiaddr 0.18.2",
+ "pin-project",
+ "rw-stream-sink",
+ "thiserror",
+]
+
+[[package]]
+name = "libp2p-allow-block-list"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55b46558c5c0bf99d3e2a1a38fd54ff5476ca66dd1737b12466a1824dd219311"
+dependencies = [
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-swarm",
+ "void",
+]
+
+[[package]]
+name = "libp2p-connection-limits"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f5107ad45cb20b2f6c3628c7b6014b996fcb13a88053f4569c872c6e30abf58"
+dependencies = [
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-swarm",
+ "void",
+]
+
+[[package]]
+name = "libp2p-core"
+version = "0.40.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd44289ab25e4c9230d9246c475a22241e301b23e8f4061d3bdef304a1a99713"
+dependencies = [
+ "either",
+ "fnv",
+ "futures",
+ "futures-timer",
+ "instant",
+ "libp2p-identity",
+ "log",
+ "multiaddr 0.18.2",
+ "multihash 0.19.3",
+ "multistream-select",
+ "once_cell",
+ "parking_lot 0.12.3",
+ "pin-project",
+ "quick-protobuf",
+ "rand",
+ "rw-stream-sink",
+ "smallvec",
+ "thiserror",
+ "unsigned-varint 0.7.2",
+ "void",
+]
+
+[[package]]
+name = "libp2p-dns"
+version = "0.40.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6a18db73084b4da2871438f6239fef35190b05023de7656e877c18a00541a3b"
+dependencies = [
+ "async-trait",
+ "futures",
+ "libp2p-core",
+ "libp2p-identity",
+ "log",
+ "parking_lot 0.12.3",
+ "smallvec",
+ "trust-dns-resolver",
+]
+
+[[package]]
+name = "libp2p-identify"
+version = "0.43.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45a96638a0a176bec0a4bcaebc1afa8cf909b114477209d7456ade52c61cd9cd"
+dependencies = [
+ "asynchronous-codec",
+ "either",
+ "futures",
+ "futures-bounded",
+ "futures-timer",
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-swarm",
+ "log",
+ "lru",
+ "quick-protobuf",
+ "quick-protobuf-codec",
+ "smallvec",
+ "thiserror",
+ "void",
+]
+
+[[package]]
+name = "libp2p-identity"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "257b5621d159b32282eac446bed6670c39c7dc68a200a992d8f056afa0066f6d"
+dependencies = [
+ "bs58 0.5.1",
+ "ed25519-dalek",
+ "hkdf",
+ "multihash 0.19.3",
+ "quick-protobuf",
+ "rand",
+ "sha2 0.10.8",
+ "thiserror",
+ "tracing",
+ "zeroize",
+]
+
+[[package]]
+name = "libp2p-kad"
+version = "0.44.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16ea178dabba6dde6ffc260a8e0452ccdc8f79becf544946692fff9d412fc29d"
+dependencies = [
+ "arrayvec",
+ "asynchronous-codec",
+ "bytes",
+ "either",
+ "fnv",
+ "futures",
+ "futures-timer",
+ "instant",
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-swarm",
+ "log",
+ "quick-protobuf",
+ "quick-protobuf-codec",
+ "rand",
+ "sha2 0.10.8",
+ "smallvec",
+ "thiserror",
+ "uint",
+ "unsigned-varint 0.7.2",
+ "void",
+]
+
+[[package]]
+name = "libp2p-mdns"
+version = "0.44.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42a2567c305232f5ef54185e9604579a894fd0674819402bb0ac0246da82f52a"
+dependencies = [
+ "data-encoding",
+ "futures",
+ "if-watch",
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-swarm",
+ "log",
+ "rand",
+ "smallvec",
+ "socket2 0.5.8",
+ "tokio",
+ "trust-dns-proto 0.22.0",
+ "void",
+]
+
+[[package]]
+name = "libp2p-metrics"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "239ba7d28f8d0b5d77760dc6619c05c7e88e74ec8fbbe97f856f20a56745e620"
+dependencies = [
+ "instant",
+ "libp2p-core",
+ "libp2p-identify",
+ "libp2p-identity",
+ "libp2p-kad",
+ "libp2p-ping",
+ "libp2p-swarm",
+ "once_cell",
+ "prometheus-client",
+]
+
+[[package]]
+name = "libp2p-noise"
+version = "0.43.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2eeec39ad3ad0677551907dd304b2f13f17208ccebe333bef194076cd2e8921"
+dependencies = [
+ "bytes",
+ "curve25519-dalek",
+ "futures",
+ "libp2p-core",
+ "libp2p-identity",
+ "log",
+ "multiaddr 0.18.2",
+ "multihash 0.19.3",
+ "once_cell",
+ "quick-protobuf",
+ "rand",
+ "sha2 0.10.8",
+ "snow",
+ "static_assertions",
+ "thiserror",
+ "x25519-dalek",
+ "zeroize",
+]
+
+[[package]]
+name = "libp2p-ping"
+version = "0.43.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e702d75cd0827dfa15f8fd92d15b9932abe38d10d21f47c50438c71dd1b5dae3"
+dependencies = [
+ "either",
+ "futures",
+ "futures-timer",
+ "instant",
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-swarm",
+ "log",
+ "rand",
+ "void",
+]
+
+[[package]]
+name = "libp2p-quic"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "130d451d83f21b81eb7b35b360bc7972aeafb15177784adc56528db082e6b927"
+dependencies = [
+ "bytes",
+ "futures",
+ "futures-timer",
+ "if-watch",
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-tls",
+ "log",
+ "parking_lot 0.12.3",
+ "quinn 0.10.2",
+ "rand",
+ "ring 0.16.20",
+ "rustls 0.21.12",
+ "socket2 0.5.8",
+ "thiserror",
+ "tokio",
+]
+
+[[package]]
+name = "libp2p-request-response"
+version = "0.25.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8e3b4d67870478db72bac87bfc260ee6641d0734e0e3e275798f089c3fecfd4"
+dependencies = [
+ "async-trait",
+ "futures",
+ "instant",
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-swarm",
+ "log",
+ "rand",
+ "smallvec",
+ "void",
+]
+
+[[package]]
+name = "libp2p-swarm"
+version = "0.43.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "580189e0074af847df90e75ef54f3f30059aedda37ea5a1659e8b9fca05c0141"
+dependencies = [
+ "either",
+ "fnv",
+ "futures",
+ "futures-timer",
+ "instant",
+ "libp2p-core",
+ "libp2p-identity",
+ "libp2p-swarm-derive",
+ "log",
+ "multistream-select",
+ "once_cell",
+ "rand",
+ "smallvec",
+ "tokio",
+ "void",
+]
+
+[[package]]
+name = "libp2p-swarm-derive"
+version = "0.33.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4d5ec2a3df00c7836d7696c136274c9c59705bac69133253696a6c932cd1d74"
+dependencies = [
+ "heck 0.4.1",
+ "proc-macro-warning 0.4.2",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "libp2p-tcp"
+version = "0.40.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b558dd40d1bcd1aaaed9de898e9ec6a436019ecc2420dd0016e712fbb61c5508"
+dependencies = [
+ "futures",
+ "futures-timer",
+ "if-watch",
+ "libc",
+ "libp2p-core",
+ "libp2p-identity",
+ "log",
+ "socket2 0.5.8",
+ "tokio",
+]
+
+[[package]]
+name = "libp2p-tls"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8218d1d5482b122ccae396bbf38abdcb283ecc96fa54760e1dfd251f0546ac61"
+dependencies = [
+ "futures",
+ "futures-rustls",
+ "libp2p-core",
+ "libp2p-identity",
+ "rcgen",
+ "ring 0.16.20",
+ "rustls 0.21.12",
+ "rustls-webpki",
+ "thiserror",
+ "x509-parser 0.15.1",
+ "yasna",
+]
+
+[[package]]
+name = "libp2p-upnp"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82775a47b34f10f787ad3e2a22e2c1541e6ebef4fe9f28f3ac553921554c94c1"
+dependencies = [
+ "futures",
+ "futures-timer",
+ "igd-next",
+ "libp2p-core",
+ "libp2p-swarm",
+ "log",
+ "tokio",
+ "void",
+]
+
+[[package]]
+name = "libp2p-wasm-ext"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e5d8e3a9e07da0ef5b55a9f26c009c8fb3c725d492d8bb4b431715786eea79c"
+dependencies = [
+ "futures",
+ "js-sys",
+ "libp2p-core",
+ "send_wrapper",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+]
+
+[[package]]
+name = "libp2p-websocket"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "004ee9c4a4631435169aee6aad2f62e3984dc031c43b6d29731e8e82a016c538"
+dependencies = [
+ "either",
+ "futures",
+ "futures-rustls",
+ "libp2p-core",
+ "libp2p-identity",
+ "log",
+ "parking_lot 0.12.3",
+ "pin-project-lite",
+ "rw-stream-sink",
+ "soketto",
+ "thiserror",
+ "url",
+ "webpki-roots",
+]
+
+[[package]]
+name = "libp2p-yamux"
+version = "0.44.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eedcb62824c4300efb9cfd4e2a6edaf3ca097b9e68b36dabe45a44469fd6a85"
+dependencies = [
+ "futures",
+ "libp2p-core",
+ "log",
+ "thiserror",
+ "yamux",
+]
+
+[[package]]
+name = "libredox"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
+dependencies = [
+ "bitflags 2.8.0",
+ "libc",
+ "redox_syscall 0.5.8",
+]
+
+[[package]]
+name = "libsecp256k1"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1"
+dependencies = [
+ "arrayref",
+ "base64 0.13.1",
+ "digest 0.9.0",
+ "hmac-drbg",
+ "libsecp256k1-core",
+ "libsecp256k1-gen-ecmult",
+ "libsecp256k1-gen-genmult",
+ "rand",
+ "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 2.6.1",
+]
+
+[[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.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9"
+dependencies = [
+ "cc",
+]
+
+[[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.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bae85b5be22d9843c80e5fc80e9b64c8a3b1f98f867c709956eca3efff4e92e2"
+dependencies = [
+ "linked-hash-map",
+]
+
+[[package]]
+name = "linregress"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9eda9dcf4f2a99787827661f312ac3219292549c2ee992bf9a6248ffb066bf7"
+dependencies = [
+ "nalgebra",
+]
+
+[[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.4.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
+
+[[package]]
+name = "lioness"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ae926706ba42c425c9457121178330d75e273df2e82e28b758faf3de3a9acb9"
+dependencies = [
+ "arrayref",
+ "blake2 0.8.1",
+ "chacha",
+ "keystream",
+]
+
+[[package]]
+name = "litemap"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
+
+[[package]]
+name = "litep2p"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f46c51c205264b834ceed95c8b195026e700494bc3991aaba3b4ea9e20626d9"
+dependencies = [
+ "async-trait",
+ "bs58 0.4.0",
+ "bytes",
+ "cid 0.10.1",
+ "ed25519-dalek",
+ "futures",
+ "futures-timer",
+ "hex-literal",
+ "indexmap 2.7.0",
+ "libc",
+ "mockall 0.12.1",
+ "multiaddr 0.17.1",
+ "multihash 0.17.0",
+ "network-interface",
+ "nohash-hasher",
+ "parking_lot 0.12.3",
+ "pin-project",
+ "prost 0.12.6",
+ "prost-build 0.11.9",
+ "quinn 0.9.4",
+ "rand",
+ "rcgen",
+ "ring 0.16.20",
+ "rustls 0.20.9",
+ "serde",
+ "sha2 0.10.8",
+ "simple-dns",
+ "smallvec",
+ "snow",
+ "socket2 0.5.8",
+ "static_assertions",
+ "str0m",
+ "thiserror",
+ "tokio",
+ "tokio-stream",
+ "tokio-tungstenite",
+ "tokio-util",
+ "tracing",
+ "trust-dns-resolver",
+ "uint",
+ "unsigned-varint 0.8.0",
+ "url",
+ "webpki",
+ "x25519-dalek",
+ "x509-parser 0.16.0",
+ "yasna",
+ "zeroize",
+]
+
+[[package]]
+name = "lock_api"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
+
+[[package]]
+name = "lru"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
+dependencies = [
+ "hashbrown 0.15.2",
+]
+
+[[package]]
+name = "lru-cache"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c"
+dependencies = [
+ "linked-hash-map",
+]
+
+[[package]]
+name = "lz4"
+version = "1.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a20b523e860d03443e98350ceaac5e71c6ba89aea7d960769ec3ce37f4de5af4"
+dependencies = [
+ "lz4-sys",
+]
+
+[[package]]
+name = "lz4-sys"
+version = "1.11.1+lz4-1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bd8c0d6c6ed0cd30b3652886bb8711dc4bb01d637a68105a3d5158039b418e6"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[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.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc33f9f0351468d26fbc53d9ce00a096c8522ecb42f19b50f34f2c422f76d21d"
+dependencies = [
+ "macro_magic_core",
+ "macro_magic_macros",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "macro_magic_core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1687dc887e42f352865a393acae7cf79d98fab6351cde1f58e9e057da89bf150"
+dependencies = [
+ "const-random",
+ "derive-syn-parse",
+ "macro_magic_core_macros",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "macro_magic_core_macros"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "macro_magic_macros"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869"
+dependencies = [
+ "macro_magic_core",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "match_cfg"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
+
+[[package]]
+name = "matchers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+dependencies = [
+ "regex-automata 0.1.10",
+]
+
+[[package]]
+name = "matches"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
+
+[[package]]
+name = "matrixmultiply"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a"
+dependencies = [
+ "autocfg",
+ "rawpointer",
+]
+
+[[package]]
+name = "md5"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "memfd"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2cffa4ad52c6f791f4f8b15f0c05f9824b2ced1160e88cc393d64fff9a8ac64"
+dependencies = [
+ "rustix 0.38.43",
+]
+
+[[package]]
+name = "memmap2"
+version = "0.5.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memmap2"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f"
+dependencies = [
+ "libc",
+]
+
+[[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.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "808b50db46293432a45e63bc15ea51e0ab4c0a1647b8eb114e31a3e698dd6fbe"
+dependencies = [
+ "hash-db",
+]
+
+[[package]]
+name = "merlin"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d"
+dependencies = [
+ "byteorder",
+ "keccak",
+ "rand_core",
+ "zeroize",
+]
+
+[[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.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
+dependencies = [
+ "adler2",
+]
+
+[[package]]
+name = "mio"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "mixnet"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daa3eb39495d8e2e2947a1d862852c90cc6a4a8845f8b41c8829cb9fcc047f4a"
+dependencies = [
+ "arrayref",
+ "arrayvec",
+ "bitflags 1.3.2",
+ "blake2 0.10.6",
+ "c2-chacha",
+ "curve25519-dalek",
+ "either",
+ "hashlink",
+ "lioness",
+ "log",
+ "parking_lot 0.12.3",
+ "rand",
+ "rand_chacha",
+ "rand_distr",
+ "subtle 2.6.1",
+ "thiserror",
+ "zeroize",
+]
+
+[[package]]
+name = "mockall"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c84490118f2ee2d74570d114f3d0493cbf02790df303d2707606c3e14e07c96"
+dependencies = [
+ "cfg-if",
+ "downcast",
+ "fragile",
+ "lazy_static",
+ "mockall_derive 0.11.4",
+ "predicates 2.1.5",
+ "predicates-tree",
+]
+
+[[package]]
+name = "mockall"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43766c2b5203b10de348ffe19f7e54564b64f3d6018ff7648d1e2d6d3a0f0a48"
+dependencies = [
+ "cfg-if",
+ "downcast",
+ "fragile",
+ "lazy_static",
+ "mockall_derive 0.12.1",
+ "predicates 3.1.3",
+ "predicates-tree",
+]
+
+[[package]]
+name = "mockall_derive"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22ce75669015c4f47b289fd4d4f56e894e4c96003ffdf3ac51313126f94c6cbb"
+dependencies = [
+ "cfg-if",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "mockall_derive"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af7cbce79ec385a1d4f54baa90a76401eb15d9cab93685f62e7e9f942aa00ae2"
+dependencies = [
+ "cfg-if",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "multi-pow"
+version = "3.0.0"
+dependencies = [
+ "log",
+ "md5",
+ "parity-scale-codec",
+ "sc-client-api",
+ "sc-consensus-pow",
+ "scale-info",
+ "sha3",
+ "sp-api",
+ "sp-consensus-pow",
+ "sp-core",
+ "sp-runtime",
+]
+
+[[package]]
+name = "multiaddr"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b36f567c7099511fa8612bbbb52dda2419ce0bdbacf31714e3a5ffdb766d3bd"
+dependencies = [
+ "arrayref",
+ "byteorder",
+ "data-encoding",
+ "log",
+ "multibase",
+ "multihash 0.17.0",
+ "percent-encoding",
+ "serde",
+ "static_assertions",
+ "unsigned-varint 0.7.2",
+ "url",
+]
+
+[[package]]
+name = "multiaddr"
+version = "0.18.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe6351f60b488e04c1d21bc69e56b89cb3f5e8f5d22557d6e8031bdfd79b6961"
+dependencies = [
+ "arrayref",
+ "byteorder",
+ "data-encoding",
+ "libp2p-identity",
+ "multibase",
+ "multihash 0.19.3",
+ "percent-encoding",
+ "serde",
+ "static_assertions",
+ "unsigned-varint 0.8.0",
+ "url",
+]
+
+[[package]]
+name = "multibase"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404"
+dependencies = [
+ "base-x",
+ "data-encoding",
+ "data-encoding-macro",
+]
+
+[[package]]
+name = "multihash"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40"
+dependencies = [
+ "blake2b_simd",
+ "blake2s_simd",
+ "blake3",
+ "core2",
+ "digest 0.10.7",
+ "multihash-derive",
+ "sha2 0.10.8",
+ "sha3",
+ "unsigned-varint 0.7.2",
+]
+
+[[package]]
+name = "multihash"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfd8a792c1694c6da4f68db0a9d707c72bd260994da179e6030a5dcee00bb815"
+dependencies = [
+ "blake2b_simd",
+ "blake2s_simd",
+ "blake3",
+ "core2",
+ "digest 0.10.7",
+ "multihash-derive",
+ "sha2 0.10.8",
+ "sha3",
+ "unsigned-varint 0.7.2",
+]
+
+[[package]]
+name = "multihash"
+version = "0.19.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d"
+dependencies = [
+ "core2",
+ "unsigned-varint 0.8.0",
+]
+
+[[package]]
+name = "multihash-derive"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db"
+dependencies = [
+ "proc-macro-crate 1.1.3",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+ "synstructure 0.12.6",
+]
+
+[[package]]
+name = "multimap"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
+
+[[package]]
+name = "multimap"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
+
+[[package]]
+name = "multistream-select"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea0df8e5eec2298a62b326ee4f0d7fe1a6b90a09dfcf9df37b38f947a8c42f19"
+dependencies = [
+ "bytes",
+ "futures",
+ "log",
+ "pin-project",
+ "smallvec",
+ "unsigned-varint 0.7.2",
+]
+
+[[package]]
+name = "nalgebra"
+version = "0.33.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26aecdf64b707efd1310e3544d709c5c0ac61c13756046aaaba41be5c4f66a3b"
+dependencies = [
+ "approx",
+ "matrixmultiply",
+ "num-complex",
+ "num-rational",
+ "num-traits",
+ "simba",
+ "typenum",
+]
+
+[[package]]
+name = "names"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bddcd3bf5144b6392de80e04c347cd7fab2508f6df16a85fc496ecd5cec39bc"
+dependencies = [
+ "rand",
+]
+
+[[package]]
+name = "netlink-packet-core"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4"
+dependencies = [
+ "anyhow",
+ "byteorder",
+ "netlink-packet-utils",
+]
+
+[[package]]
+name = "netlink-packet-route"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053998cea5a306971f88580d0829e90f270f940befd7cf928da179d4187a5a66"
+dependencies = [
+ "anyhow",
+ "bitflags 1.3.2",
+ "byteorder",
+ "libc",
+ "netlink-packet-core",
+ "netlink-packet-utils",
+]
+
+[[package]]
+name = "netlink-packet-utils"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34"
+dependencies = [
+ "anyhow",
+ "byteorder",
+ "paste",
+ "thiserror",
+]
+
+[[package]]
+name = "netlink-proto"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86b33524dc0968bfad349684447bfce6db937a9ac3332a1fe60c0c5a5ce63f21"
+dependencies = [
+ "bytes",
+ "futures",
+ "log",
+ "netlink-packet-core",
+ "netlink-sys",
+ "thiserror",
+ "tokio",
+]
+
+[[package]]
+name = "netlink-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16c903aa70590cb93691bf97a767c8d1d6122d2cc9070433deb3bbf36ce8bd23"
+dependencies = [
+ "bytes",
+ "futures",
+ "libc",
+ "log",
+ "tokio",
+]
+
+[[package]]
+name = "network-interface"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4a43439bf756eed340bdf8feba761e2d50c7d47175d87545cd5cbe4a137c4d1"
+dependencies = [
+ "cc",
+ "libc",
+ "thiserror",
+ "winapi",
+]
+
+[[package]]
+name = "nix"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b"
+dependencies = [
+ "bitflags 1.3.2",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "no-std-compat"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
+
+[[package]]
+name = "nohash-hasher"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
+
+[[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 = "nonempty"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7"
+
+[[package]]
+name = "nonzero_ext"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
+
+[[package]]
+name = "normalize-line-endings"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "num-format"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3"
+dependencies = [
+ "arrayvec",
+ "itoa",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
+dependencies = [
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
+dependencies = [
+ "hermit-abi 0.3.9",
+ "libc",
+]
+
+[[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.32.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "object"
+version = "0.36.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "oid-registry"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff"
+dependencies = [
+ "asn1-rs 0.5.2",
+]
+
+[[package]]
+name = "oid-registry"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9"
+dependencies = [
+ "asn1-rs 0.6.2",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
+
+[[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.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
+
+[[package]]
+name = "openssl"
+version = "0.10.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
+dependencies = [
+ "bitflags 2.8.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.96",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "openssl-src"
+version = "300.4.1+3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
+dependencies = [
+ "cc",
+ "libc",
+ "openssl-src",
+ "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 = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
+[[package]]
+name = "pallet-balances"
+version = "38.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "267f2b4c64e06d340fab1e48267e815dc2afaf8e6da16369b26b5c9e1e65f1aa"
+dependencies = [
+ "docify",
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "log",
+ "parity-scale-codec",
+ "scale-info",
+ "sp-runtime",
+]
+
+[[package]]
+name = "pallet-timestamp"
+version = "36.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14f264c80bef4ac3180e5cba619f319d7855cc89ba91b28b3f752620d9425b88"
+dependencies = [
+ "docify",
+ "frame-benchmarking",
+ "frame-support",
+ "frame-system",
+ "log",
+ "parity-scale-codec",
+ "scale-info",
+ "sp-inherents",
+ "sp-io",
+ "sp-runtime",
+ "sp-storage",
+ "sp-timestamp",
+]
+
+[[package]]
+name = "pallet-transaction-payment"
+version = "37.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1f02a44346aad9f3d24f2f4ae2bfe81bd858686d8c243cc77eb0ad1bf97cb04"
+dependencies = [
+ "frame-support",
+ "frame-system",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-core",
+ "sp-io",
+ "sp-runtime",
+]
+
+[[package]]
+name = "pallet-transaction-payment-rpc"
+version = "40.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2496ae1bdf86dd0aeb213d33dccd0edb4abfcead660ada070c81b254ea2cbf75"
+dependencies = [
+ "jsonrpsee",
+ "pallet-transaction-payment-rpc-runtime-api",
+ "parity-scale-codec",
+ "sp-api",
+ "sp-blockchain",
+ "sp-core",
+ "sp-rpc",
+ "sp-runtime",
+ "sp-weights",
+]
+
+[[package]]
+name = "pallet-transaction-payment-rpc-runtime-api"
+version = "37.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1879d1f608f565d590fc7520a8d9977b868a412910f6381a5ebfa45acf8abcfb"
+dependencies = [
+ "pallet-transaction-payment",
+ "parity-scale-codec",
+ "sp-api",
+ "sp-runtime",
+ "sp-weights",
+]
+
+[[package]]
+name = "parity-bip39"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e69bf016dc406eff7d53a7d3f7cf1c2e72c82b9088aac1118591e36dd2cd3e9"
+dependencies = [
+ "bitcoin_hashes",
+ "rand",
+ "rand_core",
+ "serde",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "parity-db"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "592a28a24b09c9dc20ac8afaa6839abc417c720afe42c12e1e4a9d6aa2508d2e"
+dependencies = [
+ "blake2 0.10.6",
+ "crc32fast",
+ "fs2",
+ "hex",
+ "libc",
+ "log",
+ "lz4",
+ "memmap2 0.5.10",
+ "parking_lot 0.12.3",
+ "rand",
+ "siphasher",
+ "snap",
+ "winapi",
+]
+
+[[package]]
+name = "parity-scale-codec"
+version = "3.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee"
+dependencies = [
+ "arrayvec",
+ "bitvec",
+ "byte-slice-cast",
+ "bytes",
+ "impl-trait-for-tuples",
+ "parity-scale-codec-derive",
+ "serde",
+]
+
+[[package]]
+name = "parity-scale-codec-derive"
+version = "3.6.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c"
+dependencies = [
+ "proc-macro-crate 3.2.0",
+ "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"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
+
+[[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.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+dependencies = [
+ "lock_api",
+ "parking_lot_core 0.9.10",
+]
+
+[[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.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall 0.5.8",
+ "smallvec",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "partial_sort"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7924d1d0ad836f665c9065e26d016c673ece3993f30d340068b16f282afc1156"
+
+[[package]]
+name = "password-hash"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
+dependencies = [
+ "base64ct",
+ "rand_core",
+ "subtle 2.6.1",
+]
+
+[[package]]
+name = "paste"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
+
+[[package]]
+name = "pbkdf2"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
+dependencies = [
+ "digest 0.10.7",
+ "password-hash",
+]
+
+[[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.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
+
+[[package]]
+name = "petgraph"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
+dependencies = [
+ "fixedbitset",
+ "indexmap 2.7.0",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[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.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+dependencies = [
+ "der",
+ "spki",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
+
+[[package]]
+name = "polkavm"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a3693e5efdb2bf74e449cd25fd777a28bd7ed87e41f5d5da75eb31b4de48b94"
+dependencies = [
+ "libc",
+ "log",
+ "polkavm-assembler",
+ "polkavm-common",
+ "polkavm-linux-raw",
+]
+
+[[package]]
+name = "polkavm-assembler"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fa96d6d868243acc12de813dd48e756cbadcc8e13964c70d272753266deadc1"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "polkavm-common"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d9428a5cfcc85c5d7b9fc4b6a18c4b802d0173d768182a51cc7751640f08b92"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "polkavm-derive"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae8c4bea6f3e11cd89bb18bcdddac10bd9a24015399bd1c485ad68a985a19606"
+dependencies = [
+ "polkavm-derive-impl-macro",
+]
+
+[[package]]
+name = "polkavm-derive-impl"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c4fdfc49717fb9a196e74a5d28e0bc764eb394a2c803eb11133a31ac996c60c"
+dependencies = [
+ "polkavm-common",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "polkavm-derive-impl-macro"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ba81f7b5faac81e528eb6158a6f3c9e0bb1008e0ffa19653bc8dea925ecb429"
+dependencies = [
+ "polkavm-derive-impl",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "polkavm-linker"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c7be503e60cf56c0eb785f90aaba4b583b36bff00e93997d93fef97f9553c39"
+dependencies = [
+ "gimli 0.28.1",
+ "hashbrown 0.14.5",
+ "log",
+ "object 0.32.2",
+ "polkavm-common",
+ "regalloc2 0.9.3",
+ "rustc-demangle",
+]
+
+[[package]]
+name = "polkavm-linux-raw"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26e85d3456948e650dff0cfc85603915847faf893ed1e66b020bb82ef4557120"
+
+[[package]]
+name = "polling"
+version = "3.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f"
+dependencies = [
+ "cfg-if",
+ "concurrent-queue",
+ "hermit-abi 0.4.0",
+ "pin-project-lite",
+ "rustix 0.38.43",
+ "tracing",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "poly1305"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
+dependencies = [
+ "cpufeatures",
+ "opaque-debug 0.3.1",
+ "universal-hash",
+]
+
+[[package]]
+name = "polyval"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "opaque-debug 0.3.1",
+ "universal-hash",
+]
+
+[[package]]
+name = "portable-atomic"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "predicates"
+version = "2.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59230a63c37f3e18569bdb90e4a89cbf5bf8b06fea0b84e65ea10cc4df47addd"
+dependencies = [
+ "difflib",
+ "float-cmp",
+ "itertools 0.10.5",
+ "normalize-line-endings",
+ "predicates-core",
+ "regex",
+]
+
+[[package]]
+name = "predicates"
+version = "3.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573"
+dependencies = [
+ "anstyle",
+ "predicates-core",
+]
+
+[[package]]
+name = "predicates-core"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa"
+
+[[package]]
+name = "predicates-tree"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c"
+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 = "prettyplease"
+version = "0.2.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "primitive-types"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2"
+dependencies = [
+ "fixed-hash",
+ "impl-codec",
+ "impl-serde",
+ "scale-info",
+ "uint",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a"
+dependencies = [
+ "thiserror",
+ "toml 0.5.11",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
+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-warning"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d1eaa7fa0aa1929ffdf7eeb6eac234dde6268914a14ad44d23521ab6a9b258e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "proc-macro-warning"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "834da187cfe638ae8abb0203f0b33e5ccdb02a28e7199f2f47b3e2754f50edca"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.93"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "prometheus"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1"
+dependencies = [
+ "cfg-if",
+ "fnv",
+ "lazy_static",
+ "memchr",
+ "parking_lot 0.12.3",
+ "thiserror",
+]
+
+[[package]]
+name = "prometheus-client"
+version = "0.21.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c99afa9a01501019ac3a14d71d9f94050346f55ca471ce90c799a15c58f61e2"
+dependencies = [
+ "dtoa",
+ "itoa",
+ "parking_lot 0.12.3",
+ "prometheus-client-derive-encode",
+]
+
+[[package]]
+name = "prometheus-client-derive-encode"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "prost"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd"
+dependencies = [
+ "bytes",
+ "prost-derive 0.11.9",
+]
+
+[[package]]
+name = "prost"
+version = "0.12.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29"
+dependencies = [
+ "bytes",
+ "prost-derive 0.12.6",
+]
+
+[[package]]
+name = "prost-build"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270"
+dependencies = [
+ "bytes",
+ "heck 0.4.1",
+ "itertools 0.10.5",
+ "lazy_static",
+ "log",
+ "multimap 0.8.3",
+ "petgraph",
+ "prettyplease 0.1.25",
+ "prost 0.11.9",
+ "prost-types 0.11.9",
+ "regex",
+ "syn 1.0.109",
+ "tempfile",
+ "which",
+]
+
+[[package]]
+name = "prost-build"
+version = "0.12.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4"
+dependencies = [
+ "bytes",
+ "heck 0.5.0",
+ "itertools 0.12.1",
+ "log",
+ "multimap 0.10.0",
+ "once_cell",
+ "petgraph",
+ "prettyplease 0.2.29",
+ "prost 0.12.6",
+ "prost-types 0.12.6",
+ "regex",
+ "syn 2.0.96",
+ "tempfile",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4"
+dependencies = [
+ "anyhow",
+ "itertools 0.10.5",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.12.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
+dependencies = [
+ "anyhow",
+ "itertools 0.12.1",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "prost-types"
+version = "0.11.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13"
+dependencies = [
+ "prost 0.11.9",
+]
+
+[[package]]
+name = "prost-types"
+version = "0.12.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0"
+dependencies = [
+ "prost 0.12.6",
+]
+
+[[package]]
+name = "psm"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "quanta"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e"
+dependencies = [
+ "crossbeam-utils",
+ "libc",
+ "once_cell",
+ "raw-cpuid",
+ "wasi",
+ "web-sys",
+ "winapi",
+]
+
+[[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 = "quick-protobuf-codec"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8ededb1cd78531627244d51dd0c7139fbe736c7d57af0092a76f0ffb2f56e98"
+dependencies = [
+ "asynchronous-codec",
+ "bytes",
+ "quick-protobuf",
+ "thiserror",
+ "unsigned-varint 0.7.2",
+]
+
+[[package]]
+name = "quinn"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e8b432585672228923edbbf64b8b12c14e1112f62e88737655b4a083dbcd78e"
+dependencies = [
+ "bytes",
+ "pin-project-lite",
+ "quinn-proto 0.9.6",
+ "quinn-udp 0.3.2",
+ "rustc-hash",
+ "rustls 0.20.9",
+ "thiserror",
+ "tokio",
+ "tracing",
+ "webpki",
+]
+
+[[package]]
+name = "quinn"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75"
+dependencies = [
+ "bytes",
+ "futures-io",
+ "pin-project-lite",
+ "quinn-proto 0.10.6",
+ "quinn-udp 0.4.1",
+ "rustc-hash",
+ "rustls 0.21.12",
+ "thiserror",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "quinn-proto"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94b0b33c13a79f669c85defaf4c275dc86a0c0372807d0ca3d78e0bb87274863"
+dependencies = [
+ "bytes",
+ "rand",
+ "ring 0.16.20",
+ "rustc-hash",
+ "rustls 0.20.9",
+ "slab",
+ "thiserror",
+ "tinyvec",
+ "tracing",
+ "webpki",
+]
+
+[[package]]
+name = "quinn-proto"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "141bf7dfde2fbc246bfd3fe12f2455aa24b0fbd9af535d8c86c7bd1381ff2b1a"
+dependencies = [
+ "bytes",
+ "rand",
+ "ring 0.16.20",
+ "rustc-hash",
+ "rustls 0.21.12",
+ "slab",
+ "thiserror",
+ "tinyvec",
+ "tracing",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "641538578b21f5e5c8ea733b736895576d0fe329bb883b937db6f4d163dbaaf4"
+dependencies = [
+ "libc",
+ "quinn-proto 0.9.6",
+ "socket2 0.4.10",
+ "tracing",
+ "windows-sys 0.42.0",
+]
+
+[[package]]
+name = "quinn-udp"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7"
+dependencies = [
+ "bytes",
+ "libc",
+ "socket2 0.5.8",
+ "tracing",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
+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 = "rand_distr"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
+dependencies = [
+ "num-traits",
+ "rand",
+]
+
+[[package]]
+name = "rand_pcg"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "raw-cpuid"
+version = "11.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6928fa44c097620b706542d428957635951bade7143269085389d42c8a4927e"
+dependencies = [
+ "bitflags 2.8.0",
+]
+
+[[package]]
+name = "rawpointer"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
+
+[[package]]
+name = "rayon"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "rcgen"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b"
+dependencies = [
+ "pem",
+ "ring 0.16.20",
+ "time",
+ "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.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
+dependencies = [
+ "bitflags 2.8.0",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
+dependencies = [
+ "getrandom",
+ "libredox",
+ "thiserror",
+]
+
+[[package]]
+name = "ref-cast"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf0a6f84d5f1d581da8b41b47ec8600871962f2a528115b542b362d4b744931"
+dependencies = [
+ "ref-cast-impl",
+]
+
+[[package]]
+name = "ref-cast-impl"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "regalloc2"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80535183cae11b149d618fbd3c37e38d7cda589d82d7769e196ca9a9042d7621"
+dependencies = [
+ "fxhash",
+ "log",
+ "slice-group-by",
+ "smallvec",
+]
+
+[[package]]
+name = "regalloc2"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6"
+dependencies = [
+ "hashbrown 0.13.2",
+ "log",
+ "rustc-hash",
+ "slice-group-by",
+ "smallvec",
+]
+
+[[package]]
+name = "regex"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata 0.4.9",
+ "regex-syntax 0.8.5",
+]
+
+[[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.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax 0.8.5",
+]
+
+[[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.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+
+[[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.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
+dependencies = [
+ "hmac 0.12.1",
+ "subtle 2.6.1",
+]
+
+[[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 0.7.1",
+ "web-sys",
+ "winapi",
+]
+
+[[package]]
+name = "ring"
+version = "0.17.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom",
+ "libc",
+ "spin 0.9.8",
+ "untrusted 0.9.0",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "route-recognizer"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746"
+
+[[package]]
+name = "rpassword"
+version = "7.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f"
+dependencies = [
+ "libc",
+ "rtoolbox",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "rtnetlink"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a552eb82d19f38c3beed3f786bd23aa434ceb9ac43ab44419ca6d67a7e186c0"
+dependencies = [
+ "futures",
+ "log",
+ "netlink-packet-core",
+ "netlink-packet-route",
+ "netlink-packet-utils",
+ "netlink-proto",
+ "netlink-sys",
+ "nix",
+ "thiserror",
+ "tokio",
+]
+
+[[package]]
+name = "rtoolbox"
+version = "0.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e"
+dependencies = [
+ "libc",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+
+[[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.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver 1.0.24",
+]
+
+[[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.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed"
+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.38.43"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
+dependencies = [
+ "bitflags 2.8.0",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.15",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "rustls"
+version = "0.20.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99"
+dependencies = [
+ "ring 0.16.20",
+ "sct",
+ "webpki",
+]
+
+[[package]]
+name = "rustls"
+version = "0.21.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
+dependencies = [
+ "log",
+ "ring 0.17.8",
+ "rustls-webpki",
+ "sct",
+]
+
+[[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.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
+dependencies = [
+ "base64 0.21.7",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.101.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
+dependencies = [
+ "ring 0.17.8",
+ "untrusted 0.9.0",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
+
+[[package]]
+name = "rw-stream-sink"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8c9026ff5d2f23da5e45bbc283f156383001bfb09c4e44256d02c1a685fe9a1"
+dependencies = [
+ "futures",
+ "pin-project",
+ "static_assertions",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
+name = "safe_arch"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323"
+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 = "29.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b975ee3a95eaacb611e7b415737a7fa2db4d8ad7b880cc1b97371b04e95c7903"
+dependencies = [
+ "log",
+ "sp-core",
+ "sp-wasm-interface",
+ "thiserror",
+]
+
+[[package]]
+name = "sc-basic-authorship"
+version = "0.44.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdef7ee4dd39a41957eeafb99c55749f8065a72f46c12e209ed15f4669360a6e"
+dependencies = [
+ "futures",
+ "futures-timer",
+ "log",
+ "parity-scale-codec",
+ "sc-block-builder",
+ "sc-proposer-metrics",
+ "sc-telemetry",
+ "sc-transaction-pool-api",
+ "sp-api",
+ "sp-blockchain",
+ "sp-consensus",
+ "sp-core",
+ "sp-inherents",
+ "sp-runtime",
+ "substrate-prometheus-endpoint",
+]
+
+[[package]]
+name = "sc-block-builder"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f666f8ff11f96bf6d90676739eb7ccb6a156a4507634b7af83b94f0aa8195a50"
+dependencies = [
+ "parity-scale-codec",
+ "sp-api",
+ "sp-block-builder",
+ "sp-blockchain",
+ "sp-core",
+ "sp-inherents",
+ "sp-runtime",
+ "sp-trie",
+]
+
+[[package]]
+name = "sc-chain-spec"
+version = "37.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9149a7ee8a4a799feb3ed581a288a0ce6ede42fb8b1997806f6a29997cdbd9be"
+dependencies = [
+ "array-bytes",
+ "docify",
+ "log",
+ "memmap2 0.9.5",
+ "parity-scale-codec",
+ "sc-chain-spec-derive",
+ "sc-client-api",
+ "sc-executor",
+ "sc-network",
+ "sc-telemetry",
+ "serde",
+ "serde_json",
+ "sp-blockchain",
+ "sp-core",
+ "sp-crypto-hashing",
+ "sp-genesis-builder",
+ "sp-io",
+ "sp-runtime",
+ "sp-state-machine",
+ "sp-tracing",
+]
+
+[[package]]
+name = "sc-chain-spec-derive"
+version = "12.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b18cef11d2c69703e0d7c3528202ef4ed1cd2b47a6f063e9e17cad8255b1fa94"
+dependencies = [
+ "proc-macro-crate 3.2.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "sc-cli"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b37e08bde78fa7bdf3e30682a6840236de49d2c11960535eb9a9a1a87a0fd3ab"
+dependencies = [
+ "array-bytes",
+ "chrono",
+ "clap",
+ "fdlimit",
+ "futures",
+ "itertools 0.11.0",
+ "libp2p-identity",
+ "log",
+ "names",
+ "parity-bip39",
+ "parity-scale-codec",
+ "rand",
+ "regex",
+ "rpassword",
+ "sc-client-api",
+ "sc-client-db",
+ "sc-keystore",
+ "sc-mixnet",
+ "sc-network",
+ "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",
+ "tokio",
+]
+
+[[package]]
+name = "sc-client-api"
+version = "37.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e73f1673cdfe658c4be6ffd5113b71c0de74616717e604455dcfd29e15781729"
+dependencies = [
+ "fnv",
+ "futures",
+ "log",
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "sc-executor",
+ "sc-transaction-pool-api",
+ "sc-utils",
+ "sp-api",
+ "sp-blockchain",
+ "sp-consensus",
+ "sp-core",
+ "sp-database",
+ "sp-externalities",
+ "sp-runtime",
+ "sp-state-machine",
+ "sp-statement-store",
+ "sp-storage",
+ "sp-trie",
+ "substrate-prometheus-endpoint",
+]
+
+[[package]]
+name = "sc-client-db"
+version = "0.44.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5517718f03357325c6f51a780710fac652f125316f3577d1a25a7fbdc1816db2"
+dependencies = [
+ "hash-db",
+ "kvdb",
+ "kvdb-memorydb",
+ "linked-hash-map",
+ "log",
+ "parity-db",
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "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.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "502b55375db80dea8be1336b203eb96c1e22e7c4fa7782dc775bad71688bb91c"
+dependencies = [
+ "async-trait",
+ "futures",
+ "log",
+ "mockall 0.11.4",
+ "parking_lot 0.12.3",
+ "sc-client-api",
+ "sc-network-types",
+ "sc-utils",
+ "serde",
+ "sp-api",
+ "sp-blockchain",
+ "sp-consensus",
+ "sp-core",
+ "sp-runtime",
+ "sp-state-machine",
+ "substrate-prometheus-endpoint",
+ "thiserror",
+]
+
+[[package]]
+name = "sc-consensus-aura"
+version = "0.44.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cde090c64dfcab34347bd5472b40cc608b7395ef2dd1a8403c6c13dbec74c80"
+dependencies = [
+ "async-trait",
+ "futures",
+ "log",
+ "parity-scale-codec",
+ "sc-block-builder",
+ "sc-client-api",
+ "sc-consensus",
+ "sc-consensus-slots",
+ "sc-telemetry",
+ "sp-api",
+ "sp-application-crypto",
+ "sp-block-builder",
+ "sp-blockchain",
+ "sp-consensus",
+ "sp-consensus-aura",
+ "sp-consensus-slots",
+ "sp-core",
+ "sp-inherents",
+ "sp-keystore",
+ "sp-runtime",
+ "substrate-prometheus-endpoint",
+ "thiserror",
+]
+
+[[package]]
+name = "sc-consensus-babe"
+version = "0.44.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec4aea13d44497edd2c240c85a3722c2431eaabf7f6d172891d12908504cab1f"
+dependencies = [
+ "async-trait",
+ "fork-tree",
+ "futures",
+ "log",
+ "num-bigint",
+ "num-rational",
+ "num-traits",
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "sc-client-api",
+ "sc-consensus",
+ "sc-consensus-epochs",
+ "sc-consensus-slots",
+ "sc-telemetry",
+ "sc-transaction-pool-api",
+ "sp-api",
+ "sp-application-crypto",
+ "sp-block-builder",
+ "sp-blockchain",
+ "sp-consensus",
+ "sp-consensus-babe",
+ "sp-consensus-slots",
+ "sp-core",
+ "sp-crypto-hashing",
+ "sp-inherents",
+ "sp-keystore",
+ "sp-runtime",
+ "substrate-prometheus-endpoint",
+ "thiserror",
+]
+
+[[package]]
+name = "sc-consensus-epochs"
+version = "0.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7258d517642944d4e39d11f77a413825349089e01b6f27819f4349932ff07ec"
+dependencies = [
+ "fork-tree",
+ "parity-scale-codec",
+ "sc-client-api",
+ "sc-consensus",
+ "sp-blockchain",
+ "sp-runtime",
+]
+
+[[package]]
+name = "sc-consensus-manual-seal"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc77a432b7bdd979c9f72820d214efd0eb0cac102a7dd9e23a4e0e77e5737171"
+dependencies = [
+ "assert_matches",
+ "async-trait",
+ "futures",
+ "futures-timer",
+ "jsonrpsee",
+ "log",
+ "parity-scale-codec",
+ "sc-client-api",
+ "sc-consensus",
+ "sc-consensus-aura",
+ "sc-consensus-babe",
+ "sc-consensus-epochs",
+ "sc-transaction-pool",
+ "sc-transaction-pool-api",
+ "serde",
+ "sp-api",
+ "sp-blockchain",
+ "sp-consensus",
+ "sp-consensus-aura",
+ "sp-consensus-babe",
+ "sp-consensus-slots",
+ "sp-core",
+ "sp-inherents",
+ "sp-keystore",
+ "sp-runtime",
+ "sp-timestamp",
+ "substrate-prometheus-endpoint",
+ "thiserror",
+]
+
+[[package]]
+name = "sc-consensus-pow"
+version = "0.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa2d6763ea4d0f7dbeedf7522418ee3843fb7c62712e4b8bc75cca3bf8cd0e5e"
+dependencies = [
+ "async-trait",
+ "futures",
+ "futures-timer",
+ "log",
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "sc-client-api",
+ "sc-consensus",
+ "sp-api",
+ "sp-block-builder",
+ "sp-blockchain",
+ "sp-consensus",
+ "sp-consensus-pow",
+ "sp-core",
+ "sp-inherents",
+ "sp-runtime",
+ "substrate-prometheus-endpoint",
+ "thiserror",
+]
+
+[[package]]
+name = "sc-consensus-slots"
+version = "0.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97f4aab75d55fbeee7437ed6a127a749014f831f12a0b409a71cfc3a42453ccd"
+dependencies = [
+ "async-trait",
+ "futures",
+ "futures-timer",
+ "log",
+ "parity-scale-codec",
+ "sc-client-api",
+ "sc-consensus",
+ "sc-telemetry",
+ "sp-arithmetic",
+ "sp-blockchain",
+ "sp-consensus",
+ "sp-consensus-slots",
+ "sp-core",
+ "sp-inherents",
+ "sp-runtime",
+ "sp-state-machine",
+]
+
+[[package]]
+name = "sc-executor"
+version = "0.40.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f0cc0a3728fd033589183460c5a49b2e7545d09dc89a098216ef9e9aadcd9dc"
+dependencies = [
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "sc-executor-common",
+ "sc-executor-polkavm",
+ "sc-executor-wasmtime",
+ "schnellru",
+ "sp-api",
+ "sp-core",
+ "sp-externalities",
+ "sp-io",
+ "sp-panic-handler",
+ "sp-runtime-interface",
+ "sp-trie",
+ "sp-version",
+ "sp-wasm-interface",
+ "tracing",
+]
+
+[[package]]
+name = "sc-executor-common"
+version = "0.35.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c3b703a33dcb7cddf19176fdf12294b9a6408125836b0f4afee3e6969e7f190"
+dependencies = [
+ "polkavm",
+ "sc-allocator",
+ "sp-maybe-compressed-blob",
+ "sp-wasm-interface",
+ "thiserror",
+ "wasm-instrument",
+]
+
+[[package]]
+name = "sc-executor-polkavm"
+version = "0.32.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26fe58d9cacfab73e5595fa84b80f7bd03efebe54a0574daaeb221a1d1f7ab80"
+dependencies = [
+ "log",
+ "polkavm",
+ "sc-executor-common",
+ "sp-wasm-interface",
+]
+
+[[package]]
+name = "sc-executor-wasmtime"
+version = "0.35.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cd498f2f77ec1f861c30804f5bfd796d4afcc8ce44ea1f11bfbe2847551d161"
+dependencies = [
+ "anyhow",
+ "cfg-if",
+ "libc",
+ "log",
+ "parking_lot 0.12.3",
+ "rustix 0.36.17",
+ "sc-allocator",
+ "sc-executor-common",
+ "sp-runtime-interface",
+ "sp-wasm-interface",
+ "wasmtime",
+]
+
+[[package]]
+name = "sc-informant"
+version = "0.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6dcfaffeeb2d662a26f84706132dcfd294ffd71c4077d0b4f92a6f54db184f6"
+dependencies = [
+ "ansi_term",
+ "futures",
+ "futures-timer",
+ "log",
+ "sc-client-api",
+ "sc-network",
+ "sc-network-common",
+ "sc-network-sync",
+ "sp-blockchain",
+ "sp-runtime",
+]
+
+[[package]]
+name = "sc-keystore"
+version = "33.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ebd4b5b5713006117641c049cb082e8a439dd6ac5e7b171e5cef5ce1c9f8af8"
+dependencies = [
+ "array-bytes",
+ "parking_lot 0.12.3",
+ "serde_json",
+ "sp-application-crypto",
+ "sp-core",
+ "sp-keystore",
+ "thiserror",
+]
+
+[[package]]
+name = "sc-mixnet"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04ac673840824d0357dedee5b952440b469d11f48314ff52ae59049aee7e376d"
+dependencies = [
+ "array-bytes",
+ "arrayvec",
+ "blake2 0.10.6",
+ "bytes",
+ "futures",
+ "futures-timer",
+ "log",
+ "mixnet",
+ "multiaddr 0.18.2",
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "sc-client-api",
+ "sc-network",
+ "sc-network-types",
+ "sc-transaction-pool-api",
+ "sp-api",
+ "sp-consensus",
+ "sp-core",
+ "sp-keystore",
+ "sp-mixnet",
+ "sp-runtime",
+ "thiserror",
+]
+
+[[package]]
+name = "sc-network"
+version = "0.44.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efa1cccd2bb93a008a471944f6c27cf5d0d4edc041a46f80cb440615c95c181d"
+dependencies = [
+ "array-bytes",
+ "async-channel",
+ "async-trait",
+ "asynchronous-codec",
+ "bytes",
+ "cid 0.9.0",
+ "either",
+ "fnv",
+ "futures",
+ "futures-timer",
+ "ip_network",
+ "libp2p",
+ "linked_hash_set",
+ "litep2p",
+ "log",
+ "mockall 0.11.4",
+ "once_cell",
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "partial_sort",
+ "pin-project",
+ "prost 0.12.6",
+ "prost-build 0.12.6",
+ "rand",
+ "sc-client-api",
+ "sc-network-common",
+ "sc-network-types",
+ "sc-utils",
+ "schnellru",
+ "serde",
+ "serde_json",
+ "smallvec",
+ "sp-arithmetic",
+ "sp-blockchain",
+ "sp-core",
+ "sp-runtime",
+ "substrate-prometheus-endpoint",
+ "thiserror",
+ "tokio",
+ "tokio-stream",
+ "unsigned-varint 0.7.2",
+ "void",
+ "wasm-timer",
+ "zeroize",
+]
+
+[[package]]
+name = "sc-network-common"
+version = "0.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11a8bbc9d2600f34d021796bdffbb20bdf4723f98ff3126c765b4c9363bef564"
+dependencies = [
+ "async-trait",
+ "bitflags 1.3.2",
+ "futures",
+ "libp2p-identity",
+ "parity-scale-codec",
+ "prost-build 0.12.6",
+ "sc-consensus",
+ "sc-network-types",
+ "sp-consensus",
+ "sp-consensus-grandpa",
+ "sp-runtime",
+]
+
+[[package]]
+name = "sc-network-light"
+version = "0.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18efef00b71e1a7060fb92dcc433ed4bed625a803b074e0bf4b4cf6e1d90384e"
+dependencies = [
+ "array-bytes",
+ "async-channel",
+ "futures",
+ "log",
+ "parity-scale-codec",
+ "prost 0.12.6",
+ "prost-build 0.12.6",
+ "sc-client-api",
+ "sc-network",
+ "sc-network-types",
+ "sp-blockchain",
+ "sp-core",
+ "sp-runtime",
+ "thiserror",
+]
+
+[[package]]
+name = "sc-network-sync"
+version = "0.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "628881aacdd36235d2725a7ecb13d6445c76ad470ed6e6473fc58c6b98a2417d"
+dependencies = [
+ "array-bytes",
+ "async-channel",
+ "async-trait",
+ "fork-tree",
+ "futures",
+ "futures-timer",
+ "libp2p",
+ "log",
+ "mockall 0.11.4",
+ "parity-scale-codec",
+ "prost 0.12.6",
+ "prost-build 0.12.6",
+ "sc-client-api",
+ "sc-consensus",
+ "sc-network",
+ "sc-network-common",
+ "sc-network-types",
+ "sc-utils",
+ "schnellru",
+ "smallvec",
+ "sp-arithmetic",
+ "sp-blockchain",
+ "sp-consensus",
+ "sp-consensus-grandpa",
+ "sp-core",
+ "sp-runtime",
+ "substrate-prometheus-endpoint",
+ "thiserror",
+ "tokio",
+ "tokio-stream",
+]
+
+[[package]]
+name = "sc-network-transactions"
+version = "0.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8661c677deb9159c291a4dccbdfeba3e1fe5106caea0936fb70d3765a163aa8e"
+dependencies = [
+ "array-bytes",
+ "futures",
+ "log",
+ "parity-scale-codec",
+ "sc-network",
+ "sc-network-common",
+ "sc-network-sync",
+ "sc-network-types",
+ "sc-utils",
+ "sp-consensus",
+ "sp-runtime",
+ "substrate-prometheus-endpoint",
+]
+
+[[package]]
+name = "sc-network-types"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c372dbda66644a1df0daa8c0d99c36b6f74db7dca213d2416cd84f507125224"
+dependencies = [
+ "bs58 0.5.1",
+ "ed25519-dalek",
+ "libp2p-identity",
+ "litep2p",
+ "log",
+ "multiaddr 0.18.2",
+ "multihash 0.19.3",
+ "rand",
+ "thiserror",
+ "zeroize",
+]
+
+[[package]]
+name = "sc-proposer-metrics"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f680a0bed67dab19898624246376ba85d5f70a89859ba030830aacd079c28d3c"
+dependencies = [
+ "log",
+ "substrate-prometheus-endpoint",
+]
+
+[[package]]
+name = "sc-rpc"
+version = "39.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ccc910a40803287c65194e232d99bf6e1f9200b04f8dd91433f298687b8bf3f"
+dependencies = [
+ "futures",
+ "jsonrpsee",
+ "log",
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "sc-block-builder",
+ "sc-chain-spec",
+ "sc-client-api",
+ "sc-mixnet",
+ "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-statement-store",
+ "sp-version",
+ "tokio",
+]
+
+[[package]]
+name = "sc-rpc-api"
+version = "0.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "575a098a1c59d15bec2df388437474334b76c512e9dd92b0f275801906303354"
+dependencies = [
+ "jsonrpsee",
+ "parity-scale-codec",
+ "sc-chain-spec",
+ "sc-mixnet",
+ "sc-transaction-pool-api",
+ "scale-info",
+ "serde",
+ "serde_json",
+ "sp-core",
+ "sp-rpc",
+ "sp-runtime",
+ "sp-version",
+ "thiserror",
+]
+
+[[package]]
+name = "sc-rpc-server"
+version = "16.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c14c236a01e03f55f16b92d89fd902cf2e4e9887357a3c36827a1e39b799c6b"
+dependencies = [
+ "forwarded-header-value",
+ "futures",
+ "governor",
+ "http 1.2.0",
+ "http-body-util",
+ "hyper 1.5.2",
+ "ip_network",
+ "jsonrpsee",
+ "log",
+ "serde",
+ "serde_json",
+ "substrate-prometheus-endpoint",
+ "tokio",
+ "tower",
+ "tower-http",
+]
+
+[[package]]
+name = "sc-rpc-spec-v2"
+version = "0.44.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "934087c0aae2642327e07070ae3739ae82bbadaf876fadcff0c9b19c37a87ada"
+dependencies = [
+ "array-bytes",
+ "futures",
+ "futures-util",
+ "hex",
+ "jsonrpsee",
+ "log",
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "rand",
+ "sc-chain-spec",
+ "sc-client-api",
+ "sc-rpc",
+ "sc-transaction-pool-api",
+ "sc-utils",
+ "schnellru",
+ "serde",
+ "sp-api",
+ "sp-blockchain",
+ "sp-core",
+ "sp-rpc",
+ "sp-runtime",
+ "sp-version",
+ "thiserror",
+ "tokio",
+ "tokio-stream",
+]
+
+[[package]]
+name = "sc-service"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd9eb103478c93e3a9325fb9c07d2c6a507cd04934954c930fc33a1e0791010b"
+dependencies = [
+ "async-trait",
+ "directories",
+ "exit-future",
+ "futures",
+ "futures-timer",
+ "jsonrpsee",
+ "log",
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "pin-project",
+ "rand",
+ "sc-chain-spec",
+ "sc-client-api",
+ "sc-client-db",
+ "sc-consensus",
+ "sc-executor",
+ "sc-informant",
+ "sc-keystore",
+ "sc-network",
+ "sc-network-common",
+ "sc-network-light",
+ "sc-network-sync",
+ "sc-network-transactions",
+ "sc-network-types",
+ "sc-rpc",
+ "sc-rpc-server",
+ "sc-rpc-spec-v2",
+ "sc-sysinfo",
+ "sc-telemetry",
+ "sc-tracing",
+ "sc-transaction-pool",
+ "sc-transaction-pool-api",
+ "sc-utils",
+ "schnellru",
+ "serde",
+ "serde_json",
+ "sp-api",
+ "sp-blockchain",
+ "sp-consensus",
+ "sp-core",
+ "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-state-db"
+version = "0.36.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f689d0b97c1bbdb2ca31b5f202bda195947f85c7fef990651cad202b99de896b"
+dependencies = [
+ "log",
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "sp-core",
+]
+
+[[package]]
+name = "sc-sysinfo"
+version = "37.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2ce11152bd7a2b01713e71de71a5610067bd1b3509aa207e3d87f5ee53dd328"
+dependencies = [
+ "derive_more 0.99.18",
+ "futures",
+ "libc",
+ "log",
+ "rand",
+ "rand_pcg",
+ "regex",
+ "sc-telemetry",
+ "serde",
+ "serde_json",
+ "sp-core",
+ "sp-crypto-hashing",
+ "sp-io",
+ "sp-std",
+]
+
+[[package]]
+name = "sc-telemetry"
+version = "24.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b59589eadf05088221cc60b6d9f68f89208262ae9b1e4fb8704eefe7de48845"
+dependencies = [
+ "chrono",
+ "futures",
+ "libp2p",
+ "log",
+ "parking_lot 0.12.3",
+ "pin-project",
+ "rand",
+ "sc-network",
+ "sc-utils",
+ "serde",
+ "serde_json",
+ "thiserror",
+ "wasm-timer",
+]
+
+[[package]]
+name = "sc-tracing"
+version = "37.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2604130246c4f6c2a2633f320bde95e7122383385c779b263eb03b714d92758a"
+dependencies = [
+ "chrono",
+ "console",
+ "is-terminal",
+ "lazy_static",
+ "libc",
+ "log",
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "rustc-hash",
+ "sc-client-api",
+ "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 = "11.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "151cdf86d79abf22cf2a240a7ca95041c908dbd96c2ae9a818073042aa210964"
+dependencies = [
+ "proc-macro-crate 3.2.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "sc-transaction-pool"
+version = "37.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f716ef0dc78458f6ecb831cdb3b60ec804c1ed93313d7f98661beb5438dbbf71"
+dependencies = [
+ "async-trait",
+ "futures",
+ "futures-timer",
+ "linked-hash-map",
+ "log",
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "sc-client-api",
+ "sc-transaction-pool-api",
+ "sc-utils",
+ "serde",
+ "sp-api",
+ "sp-blockchain",
+ "sp-core",
+ "sp-crypto-hashing",
+ "sp-runtime",
+ "sp-tracing",
+ "sp-transaction-pool",
+ "substrate-prometheus-endpoint",
+ "thiserror",
+]
+
+[[package]]
+name = "sc-transaction-pool-api"
+version = "37.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f02936289a079360935685eee5400311994b25e9edb2420a3c4247d419a77f46"
+dependencies = [
+ "async-trait",
+ "futures",
+ "log",
+ "parity-scale-codec",
+ "serde",
+ "sp-blockchain",
+ "sp-core",
+ "sp-runtime",
+ "thiserror",
+]
+
+[[package]]
+name = "sc-utils"
+version = "17.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "acf1bad736c230f16beb1cf48af9e69564df23b13aca9e5751a61266340b4bb5"
+dependencies = [
+ "async-channel",
+ "futures",
+ "futures-timer",
+ "lazy_static",
+ "log",
+ "parking_lot 0.12.3",
+ "prometheus",
+ "sp-arithmetic",
+]
+
+[[package]]
+name = "scale-info"
+version = "2.11.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b"
+dependencies = [
+ "bitvec",
+ "cfg-if",
+ "derive_more 1.0.0",
+ "parity-scale-codec",
+ "scale-info-derive",
+ "serde",
+]
+
+[[package]]
+name = "scale-info-derive"
+version = "2.11.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf"
+dependencies = [
+ "proc-macro-crate 3.2.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "schannel"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "schnellru"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "356285bbf17bea63d9e52e96bd18f039672ac92b55b8cb997d6162a2a37d1649"
+dependencies = [
+ "ahash",
+ "cfg-if",
+ "hashbrown 0.13.2",
+]
+
+[[package]]
+name = "schnorrkel"
+version = "0.11.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de18f6d8ba0aad7045f5feae07ec29899c1112584a38509a84ad7b04451eaa0"
+dependencies = [
+ "aead",
+ "arrayref",
+ "arrayvec",
+ "curve25519-dalek",
+ "getrandom_or_panic",
+ "merlin",
+ "rand_core",
+ "serde_bytes",
+ "sha2 0.10.8",
+ "subtle 2.6.1",
+ "zeroize",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "scratch"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152"
+
+[[package]]
+name = "sct"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
+dependencies = [
+ "ring 0.17.8",
+ "untrusted 0.9.0",
+]
+
+[[package]]
+name = "sctp-proto"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6220f78bb44c15f326b0596113305f6101097a18755d53727a575c97e09fb24"
+dependencies = [
+ "bytes",
+ "crc",
+ "fxhash",
+ "log",
+ "rand",
+ "slab",
+ "thiserror",
+]
+
+[[package]]
+name = "sec1"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
+dependencies = [
+ "base16ct",
+ "der",
+ "generic-array 0.14.7",
+ "pkcs8",
+ "serdect",
+ "subtle 2.6.1",
+ "zeroize",
+]
+
+[[package]]
+name = "secp256k1"
+version = "0.28.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10"
+dependencies = [
+ "secp256k1-sys",
+]
+
+[[package]]
+name = "secp256k1-sys"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb"
+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.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
+dependencies = [
+ "bitflags 2.8.0",
+ "core-foundation",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "semver"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537"
+dependencies = [
+ "semver-parser",
+]
+
+[[package]]
+name = "semver"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+
+[[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.217"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_bytes"
+version = "0.11.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.217"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.135"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "serdect"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177"
+dependencies = [
+ "base16ct",
+ "serde",
+]
+
+[[package]]
+name = "sha-1"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest 0.10.7",
+ "sha1-asm",
+]
+
+[[package]]
+name = "sha1"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest 0.10.7",
+]
+
+[[package]]
+name = "sha1-asm"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "286acebaf8b67c1130aedffad26f594eff0c1292389158135327d2e23aed582b"
+dependencies = [
+ "cc",
+]
+
+[[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.1",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+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.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "digest 0.10.7",
+ "rand_core",
+]
+
+[[package]]
+name = "simba"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa"
+dependencies = [
+ "approx",
+ "num-complex",
+ "num-traits",
+ "paste",
+ "wide",
+]
+
+[[package]]
+name = "simple-dns"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cae9a3fcdadafb6d97f4c0e007e4247b114ee0f119f650c3cbf3a8b3a1479694"
+dependencies = [
+ "bitflags 2.8.0",
+]
+
+[[package]]
+name = "simple-mermaid"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "620a1d43d70e142b1d46a929af51d44f383db9c7a2ec122de2cd992ccfcf3c18"
+
+[[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.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+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.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
+
+[[package]]
+name = "snap"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b"
+
+[[package]]
+name = "snow"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "850948bee068e713b8ab860fe1adc4d109676ab4c3b621fd8147f06b261f2f85"
+dependencies = [
+ "aes-gcm",
+ "blake2 0.10.6",
+ "chacha20poly1305",
+ "curve25519-dalek",
+ "rand_core",
+ "ring 0.17.8",
+ "rustc_version",
+ "sha2 0.10.8",
+ "subtle 2.6.1",
+]
+
+[[package]]
+name = "socket2"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "socket2"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "soketto"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721"
+dependencies = [
+ "base64 0.22.1",
+ "bytes",
+ "futures",
+ "http 1.2.0",
+ "httparse",
+ "log",
+ "rand",
+ "sha1",
+]
+
+[[package]]
+name = "sp-api"
+version = "34.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbce492e0482134128b7729ea36f5ef1a9f9b4de2d48ff8dde7b5e464e28ce75"
+dependencies = [
+ "docify",
+ "hash-db",
+ "log",
+ "parity-scale-codec",
+ "scale-info",
+ "sp-api-proc-macro",
+ "sp-core",
+ "sp-externalities",
+ "sp-metadata-ir",
+ "sp-runtime",
+ "sp-runtime-interface",
+ "sp-state-machine",
+ "sp-trie",
+ "sp-version",
+ "thiserror",
+]
+
+[[package]]
+name = "sp-api-proc-macro"
+version = "20.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9aadf9e97e694f0e343978aa632938c5de309cbcc8afed4136cb71596737278"
+dependencies = [
+ "Inflector",
+ "blake2 0.10.6",
+ "expander",
+ "proc-macro-crate 3.2.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "sp-application-crypto"
+version = "38.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8133012faa5f75b2f0b1619d9f720c1424ac477152c143e5f7dbde2fe1a958"
+dependencies = [
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-core",
+ "sp-io",
+]
+
+[[package]]
+name = "sp-arithmetic"
+version = "26.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46d0d0a4c591c421d3231ddd5e27d828618c24456d51445d21a1f79fcee97c23"
+dependencies = [
+ "docify",
+ "integer-sqrt",
+ "num-traits",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-std",
+ "static_assertions",
+]
+
+[[package]]
+name = "sp-block-builder"
+version = "34.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74738809461e3d4bd707b5b94e0e0c064a623a74a6a8fe5c98514417a02858dd"
+dependencies = [
+ "sp-api",
+ "sp-inherents",
+ "sp-runtime",
+]
+
+[[package]]
+name = "sp-blockchain"
+version = "37.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a309eecd6b5689f57e67181deaa628d9c8951db1ba0d26f07c69e14dffdc4765"
+dependencies = [
+ "futures",
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "schnellru",
+ "sp-api",
+ "sp-consensus",
+ "sp-core",
+ "sp-database",
+ "sp-runtime",
+ "sp-state-machine",
+ "thiserror",
+ "tracing",
+]
+
+[[package]]
+name = "sp-consensus"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce75efd1e164be667a53c20182c45b4c2abe325abcbd21fc292b82be5b9240f7"
+dependencies = [
+ "async-trait",
+ "futures",
+ "log",
+ "sp-core",
+ "sp-inherents",
+ "sp-runtime",
+ "sp-state-machine",
+ "thiserror",
+]
+
+[[package]]
+name = "sp-consensus-aura"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a8faaa05bbcb9c41f0cc535c4c1315abf6df472b53eae018678d1b4d811ac47"
+dependencies = [
+ "async-trait",
+ "parity-scale-codec",
+ "scale-info",
+ "sp-api",
+ "sp-application-crypto",
+ "sp-consensus-slots",
+ "sp-inherents",
+ "sp-runtime",
+ "sp-timestamp",
+]
+
+[[package]]
+name = "sp-consensus-babe"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36ee95e17ee8dcd14db7d584b899a426565ca9abe5a266ab82277977fc547f86"
+dependencies = [
+ "async-trait",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-api",
+ "sp-application-crypto",
+ "sp-consensus-slots",
+ "sp-core",
+ "sp-inherents",
+ "sp-runtime",
+ "sp-timestamp",
+]
+
+[[package]]
+name = "sp-consensus-grandpa"
+version = "21.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "587b791efe6c5f18e09dbbaf1ece0ee7b5fe51602c233e7151a3676b0de0260b"
+dependencies = [
+ "finality-grandpa",
+ "log",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-api",
+ "sp-application-crypto",
+ "sp-core",
+ "sp-keystore",
+ "sp-runtime",
+]
+
+[[package]]
+name = "sp-consensus-pow"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fa6b7d199a1c16cea1b74ee7cee174bf08f2120ab66a87bee7b12353100b47c"
+dependencies = [
+ "parity-scale-codec",
+ "sp-api",
+ "sp-core",
+ "sp-runtime",
+]
+
+[[package]]
+name = "sp-consensus-slots"
+version = "0.40.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbafb7ed44f51c22fa277fb39b33dc601fa426133a8e2b53f3f46b10f07fba43"
+dependencies = [
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-timestamp",
+]
+
+[[package]]
+name = "sp-core"
+version = "34.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c961a5e33fb2962fa775c044ceba43df9c6f917e2c35d63bfe23738468fa76a7"
+dependencies = [
+ "array-bytes",
+ "bitflags 1.3.2",
+ "blake2 0.10.6",
+ "bounded-collections",
+ "bs58 0.5.1",
+ "dyn-clonable",
+ "ed25519-zebra",
+ "futures",
+ "hash-db",
+ "hash256-std-hasher",
+ "impl-serde",
+ "itertools 0.11.0",
+ "k256",
+ "libsecp256k1",
+ "log",
+ "merlin",
+ "parity-bip39",
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "paste",
+ "primitive-types",
+ "rand",
+ "scale-info",
+ "schnorrkel",
+ "secp256k1",
+ "secrecy",
+ "serde",
+ "sp-crypto-hashing",
+ "sp-debug-derive",
+ "sp-externalities",
+ "sp-runtime-interface",
+ "sp-std",
+ "sp-storage",
+ "ss58-registry",
+ "substrate-bip39",
+ "thiserror",
+ "tracing",
+ "w3f-bls",
+ "zeroize",
+]
+
+[[package]]
+name = "sp-crypto-hashing"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc9927a7f81334ed5b8a98a4a978c81324d12bd9713ec76b5c68fd410174c5eb"
+dependencies = [
+ "blake2b_simd",
+ "byteorder",
+ "digest 0.10.7",
+ "sha2 0.10.8",
+ "sha3",
+ "twox-hash",
+]
+
+[[package]]
+name = "sp-crypto-hashing-proc-macro"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b85d0f1f1e44bd8617eb2a48203ee854981229e3e79e6f468c7175d5fd37489b"
+dependencies = [
+ "quote",
+ "sp-crypto-hashing",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "sp-database"
+version = "10.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "722cbecdbf5b94578137dbd07feb51e95f7de221be0c1ff4dcfe0bb4cd986929"
+dependencies = [
+ "kvdb",
+ "parking_lot 0.12.3",
+]
+
+[[package]]
+name = "sp-debug-derive"
+version = "14.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "sp-externalities"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a904407d61cb94228c71b55a9d3708e9d6558991f9e83bd42bd91df37a159d30"
+dependencies = [
+ "environmental",
+ "parity-scale-codec",
+ "sp-storage",
+]
+
+[[package]]
+name = "sp-genesis-builder"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a646ed222fd86d5680faa4a8967980eb32f644cae6c8523e1c689a6deda3e8"
+dependencies = [
+ "parity-scale-codec",
+ "scale-info",
+ "serde_json",
+ "sp-api",
+ "sp-runtime",
+]
+
+[[package]]
+name = "sp-inherents"
+version = "34.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afffbddc380d99a90c459ba1554bbbc01d62e892de9f1485af6940b89c4c0d57"
+dependencies = [
+ "async-trait",
+ "impl-trait-for-tuples",
+ "parity-scale-codec",
+ "scale-info",
+ "sp-runtime",
+ "thiserror",
+]
+
+[[package]]
+name = "sp-io"
+version = "38.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ef7eb561bb4839cc8424ce58c5ea236cbcca83f26fcc0426d8decfe8aa97d4"
+dependencies = [
+ "bytes",
+ "docify",
+ "ed25519-dalek",
+ "libsecp256k1",
+ "log",
+ "parity-scale-codec",
+ "polkavm-derive",
+ "rustversion",
+ "secp256k1",
+ "sp-core",
+ "sp-crypto-hashing",
+ "sp-externalities",
+ "sp-keystore",
+ "sp-runtime-interface",
+ "sp-state-machine",
+ "sp-tracing",
+ "sp-trie",
+ "tracing",
+ "tracing-core",
+]
+
+[[package]]
+name = "sp-keyring"
+version = "39.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c0e20624277f578b27f44ecfbe2ebc2e908488511ee2c900c5281599f700ab3"
+dependencies = [
+ "sp-core",
+ "sp-runtime",
+ "strum 0.26.3",
+]
+
+[[package]]
+name = "sp-keystore"
+version = "0.40.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0248b4d784cb4a01472276928977121fa39d977a5bb24793b6b15e64b046df42"
+dependencies = [
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "sp-core",
+ "sp-externalities",
+]
+
+[[package]]
+name = "sp-maybe-compressed-blob"
+version = "11.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0c768c11afbe698a090386876911da4236af199cd38a5866748df4d8628aeff"
+dependencies = [
+ "thiserror",
+ "zstd 0.12.4",
+]
+
+[[package]]
+name = "sp-metadata-ir"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a616fa51350b35326682a472ee8e6ba742fdacb18babac38ecd46b3e05ead869"
+dependencies = [
+ "frame-metadata",
+ "parity-scale-codec",
+ "scale-info",
+]
+
+[[package]]
+name = "sp-mixnet"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b0b017dd54823b6e62f9f7171a1df350972e5c6d0bf17e0c2f78680b5c31942"
+dependencies = [
+ "parity-scale-codec",
+ "scale-info",
+ "sp-api",
+ "sp-application-crypto",
+]
+
+[[package]]
+name = "sp-offchain"
+version = "34.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d9de237d72ecffd07f90826eef18360208b16d8de939d54e61591fac0fcbf99"
+dependencies = [
+ "sp-api",
+ "sp-core",
+ "sp-runtime",
+]
+
+[[package]]
+name = "sp-panic-handler"
+version = "13.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81478b3740b357fa0ea10fcdc1ee02ebae7734e50f80342c4743476d9f78eeea"
+dependencies = [
+ "backtrace",
+ "regex",
+]
+
+[[package]]
+name = "sp-rpc"
+version = "32.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45458f0955870a92b3969098d4f1f4e9b55b4282d9f1dc112a51bb5bb6584900"
+dependencies = [
+ "rustc-hash",
+ "serde",
+ "sp-core",
+]
+
+[[package]]
+name = "sp-runtime"
+version = "39.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1e00503b83cf48fffe48746b91b9b832d6785d4e2eeb0941558371eac6baac6"
+dependencies = [
+ "docify",
+ "either",
+ "hash256-std-hasher",
+ "impl-trait-for-tuples",
+ "log",
+ "num-traits",
+ "parity-scale-codec",
+ "paste",
+ "rand",
+ "scale-info",
+ "serde",
+ "simple-mermaid",
+ "sp-application-crypto",
+ "sp-arithmetic",
+ "sp-core",
+ "sp-io",
+ "sp-std",
+ "sp-weights",
+ "tracing",
+]
+
+[[package]]
+name = "sp-runtime-interface"
+version = "28.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "985eb981f40c689c6a0012c937b68ed58dabb4341d06f2dfe4dfd5ed72fa4017"
+dependencies = [
+ "bytes",
+ "impl-trait-for-tuples",
+ "parity-scale-codec",
+ "polkavm-derive",
+ "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 = "18.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0195f32c628fee3ce1dfbbf2e7e52a30ea85f3589da9fe62a8b816d70fc06294"
+dependencies = [
+ "Inflector",
+ "expander",
+ "proc-macro-crate 3.2.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "sp-session"
+version = "35.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d04fcd2d1270038be94d00103e8e95f7fbab9075dcc78096b91d8931ee970d73"
+dependencies = [
+ "parity-scale-codec",
+ "scale-info",
+ "sp-api",
+ "sp-core",
+ "sp-keystore",
+ "sp-runtime",
+ "sp-staking",
+]
+
+[[package]]
+name = "sp-staking"
+version = "34.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "143a764cacbab58347d8b2fd4c8909031fb0888d7b02a0ec9fa44f81f780d732"
+dependencies = [
+ "impl-trait-for-tuples",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "sp-core",
+ "sp-runtime",
+]
+
+[[package]]
+name = "sp-state-machine"
+version = "0.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "930104d6ae882626e8880d9b1578da9300655d337a3ffb45e130c608b6c89660"
+dependencies = [
+ "hash-db",
+ "log",
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "rand",
+ "smallvec",
+ "sp-core",
+ "sp-externalities",
+ "sp-panic-handler",
+ "sp-trie",
+ "thiserror",
+ "tracing",
+ "trie-db",
+]
+
+[[package]]
+name = "sp-statement-store"
+version = "18.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c219bc34ef4d1f9835f3ed881f965643c32034fcc030eb33b759dadbc802c1c2"
+dependencies = [
+ "aes-gcm",
+ "curve25519-dalek",
+ "ed25519-dalek",
+ "hkdf",
+ "parity-scale-codec",
+ "rand",
+ "scale-info",
+ "sha2 0.10.8",
+ "sp-api",
+ "sp-application-crypto",
+ "sp-core",
+ "sp-crypto-hashing",
+ "sp-externalities",
+ "sp-runtime",
+ "sp-runtime-interface",
+ "thiserror",
+ "x25519-dalek",
+]
+
+[[package]]
+name = "sp-std"
+version = "14.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12f8ee986414b0a9ad741776762f4083cd3a5128449b982a3919c4df36874834"
+
+[[package]]
+name = "sp-storage"
+version = "21.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99c82989b3a4979a7e1ad848aad9f5d0b4388f1f454cc131766526601ab9e8f8"
+dependencies = [
+ "impl-serde",
+ "parity-scale-codec",
+ "ref-cast",
+ "serde",
+ "sp-debug-derive",
+]
+
+[[package]]
+name = "sp-timestamp"
+version = "34.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72a1cb4df653d62ccc0dbce1db45d1c9443ec60247ee9576962d24da4c9c6f07"
+dependencies = [
+ "async-trait",
+ "parity-scale-codec",
+ "sp-inherents",
+ "sp-runtime",
+ "thiserror",
+]
+
+[[package]]
+name = "sp-tracing"
+version = "17.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf641a1d17268c8fcfdb8e0fa51a79c2d4222f4cfda5f3944dbdbc384dced8d5"
+dependencies = [
+ "parity-scale-codec",
+ "tracing",
+ "tracing-core",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "sp-transaction-pool"
+version = "34.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc4bf251059485a7dd38fe4afeda8792983511cc47f342ff4695e2dcae6b5247"
+dependencies = [
+ "sp-api",
+ "sp-runtime",
+]
+
+[[package]]
+name = "sp-transaction-storage-proof"
+version = "34.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c765c2e9817d95f13d42a9f2295c60723464669765c6e5acbacebd2f54932f67"
+dependencies = [
+ "async-trait",
+ "parity-scale-codec",
+ "scale-info",
+ "sp-core",
+ "sp-inherents",
+ "sp-runtime",
+ "sp-trie",
+]
+
+[[package]]
+name = "sp-trie"
+version = "37.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6282aef9f4b6ecd95a67a45bcdb67a71f4a4155c09a53c10add4ffe823db18cd"
+dependencies = [
+ "ahash",
+ "hash-db",
+ "lazy_static",
+ "memory-db",
+ "nohash-hasher",
+ "parity-scale-codec",
+ "parking_lot 0.12.3",
+ "rand",
+ "scale-info",
+ "schnellru",
+ "sp-core",
+ "sp-externalities",
+ "thiserror",
+ "tracing",
+ "trie-db",
+ "trie-root",
+]
+
+[[package]]
+name = "sp-version"
+version = "37.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d521a405707b5be561367cd3d442ff67588993de24062ce3adefcf8437ee9fe1"
+dependencies = [
+ "impl-serde",
+ "parity-scale-codec",
+ "parity-wasm",
+ "scale-info",
+ "serde",
+ "sp-crypto-hashing-proc-macro",
+ "sp-runtime",
+ "sp-std",
+ "sp-version-proc-macro",
+ "thiserror",
+]
+
+[[package]]
+name = "sp-version-proc-macro"
+version = "14.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aee8f6730641a65fcf0c8f9b1e448af4b3bb083d08058b47528188bccc7b7a7"
+dependencies = [
+ "parity-scale-codec",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "sp-wasm-interface"
+version = "21.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b066baa6d57951600b14ffe1243f54c47f9c23dd89c262e17ca00ae8dca58be9"
+dependencies = [
+ "anyhow",
+ "impl-trait-for-tuples",
+ "log",
+ "parity-scale-codec",
+ "wasmtime",
+]
+
+[[package]]
+name = "sp-weights"
+version = "31.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93cdaf72a1dad537bbb130ba4d47307ebe5170405280ed1aa31fa712718a400e"
+dependencies = [
+ "bounded-collections",
+ "parity-scale-codec",
+ "scale-info",
+ "serde",
+ "smallvec",
+ "sp-arithmetic",
+ "sp-debug-derive",
+]
+
+[[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 = "spinning_top"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
+name = "spki"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
+dependencies = [
+ "base64ct",
+ "der",
+]
+
+[[package]]
+name = "ss58-registry"
+version = "1.51.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19409f13998e55816d1c728395af0b52ec066206341d939e22e7766df9b494b8"
+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.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1389c88ddd739ec6d3f8f83343764a0e944cd23cfbf126a9796a714b0b6edd6f"
+dependencies = [
+ "cfg_aliases",
+ "memchr",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "str0m"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6706347e49b13373f7ddfafad47df7583ed52083d6fc8a594eb2c80497ef959d"
+dependencies = [
+ "combine",
+ "crc",
+ "fastrand",
+ "hmac 0.12.1",
+ "once_cell",
+ "openssl",
+ "openssl-sys",
+ "sctp-proto",
+ "serde",
+ "sha-1",
+ "thiserror",
+ "tracing",
+]
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "strum"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
+
+[[package]]
+name = "strum"
+version = "0.26.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
+dependencies = [
+ "strum_macros 0.26.4",
+]
+
+[[package]]
+name = "strum_macros"
+version = "0.24.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
+dependencies = [
+ "heck 0.4.1",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "strum_macros"
+version = "0.26.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
+dependencies = [
+ "heck 0.5.0",
+ "proc-macro2",
+ "quote",
+ "rustversion",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "substrate-bip39"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca58ffd742f693dc13d69bdbb2e642ae239e0053f6aab3b104252892f856700a"
+dependencies = [
+ "hmac 0.12.1",
+ "pbkdf2",
+ "schnorrkel",
+ "sha2 0.10.8",
+ "zeroize",
+]
+
+[[package]]
+name = "substrate-build-script-utils"
+version = "11.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b285e7d183a32732fdc119f3d81b7915790191fad602b7c709ef247073c77a2e"
+
+[[package]]
+name = "substrate-frame-rpc-system"
+version = "38.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdf4468637471dd481811d0d1ffaf8e8a98165d9ad6b586bfb2911ca1cb081f5"
+dependencies = [
+ "docify",
+ "frame-system-rpc-runtime-api",
+ "futures",
+ "jsonrpsee",
+ "log",
+ "parity-scale-codec",
+ "sc-rpc-api",
+ "sc-transaction-pool-api",
+ "sp-api",
+ "sp-block-builder",
+ "sp-blockchain",
+ "sp-core",
+ "sp-runtime",
+]
+
+[[package]]
+name = "substrate-prometheus-endpoint"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b6772e9c3621b8d067a706dfda6a20db6faa7cde39822c58ffc8588d16409c5"
+dependencies = [
+ "http-body-util",
+ "hyper 1.5.2",
+ "hyper-util",
+ "log",
+ "prometheus",
+ "thiserror",
+ "tokio",
+]
+
+[[package]]
+name = "substrate-wasm-builder"
+version = "24.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf035ffe7335fb24053edfe4d0a5780250eda772082a1b80ae25835dd4c09265"
+dependencies = [
+ "build-helper",
+ "cargo_metadata",
+ "console",
+ "filetime",
+ "jobserver",
+ "parity-wasm",
+ "polkavm-linker",
+ "sp-maybe-compressed-blob",
+ "strum 0.26.3",
+ "tempfile",
+ "toml 0.8.19",
+ "walkdir",
+ "wasm-opt",
+]
+
+[[package]]
+name = "subtle"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
+
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[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.96"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
+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 1.0.109",
+ "unicode-xid",
+]
+
+[[package]]
+name = "synstructure"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "system-configuration"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
+dependencies = [
+ "bitflags 2.8.0",
+ "core-foundation",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
+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.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
+
+[[package]]
+name = "tempfile"
+version = "3.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "getrandom",
+ "once_cell",
+ "rustix 0.38.43",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "terminal_size"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9"
+dependencies = [
+ "rustix 0.38.43",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "termtree"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+]
+
+[[package]]
+name = "time"
+version = "0.3.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
+dependencies = [
+ "deranged",
+ "itoa",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
+
+[[package]]
+name = "time-macros"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
+dependencies = [
+ "num-conv",
+ "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 = "tinystr"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8"
+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.43.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "libc",
+ "mio",
+ "parking_lot 0.12.3",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2 0.5.8",
+ "tokio-macros",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.24.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
+dependencies = [
+ "rustls 0.21.12",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+ "tokio-util",
+]
+
+[[package]]
+name = "tokio-tungstenite"
+version = "0.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
+dependencies = [
+ "futures-util",
+ "log",
+ "rustls 0.21.12",
+ "rustls-native-certs",
+ "tokio",
+ "tokio-rustls",
+ "tungstenite",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-io",
+ "futures-sink",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.22.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
+dependencies = [
+ "indexmap 2.7.0",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "winnow",
+]
+
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project",
+ "pin-project-lite",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
+dependencies = [
+ "bitflags 2.8.0",
+ "bytes",
+ "http 1.2.0",
+ "http-body 1.0.1",
+ "http-body-util",
+ "pin-project-lite",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
+[[package]]
+name = "tracing"
+version = "0.1.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
+dependencies = [
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
+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-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "parking_lot 0.12.3",
+ "regex",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "time",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "trie-db"
+version = "0.29.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c992b4f40c234a074d48a757efeabb1a6be88af84c0c23f7ca158950cb0ae7f"
+dependencies = [
+ "hash-db",
+ "log",
+ "rustc-hex",
+ "smallvec",
+]
+
+[[package]]
+name = "trie-root"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+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 0.5.1",
+ "futures-channel",
+ "futures-io",
+ "futures-util",
+ "idna 0.2.3",
+ "ipnet",
+ "lazy_static",
+ "rand",
+ "smallvec",
+ "socket2 0.4.10",
+ "thiserror",
+ "tinyvec",
+ "tokio",
+ "tracing",
+ "url",
+]
+
+[[package]]
+name = "trust-dns-proto"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3119112651c157f4488931a01e586aa459736e9d6046d3bd9105ffb69352d374"
+dependencies = [
+ "async-trait",
+ "cfg-if",
+ "data-encoding",
+ "enum-as-inner 0.6.1",
+ "futures-channel",
+ "futures-io",
+ "futures-util",
+ "idna 0.4.0",
+ "ipnet",
+ "once_cell",
+ "rand",
+ "smallvec",
+ "thiserror",
+ "tinyvec",
+ "tokio",
+ "tracing",
+ "url",
+]
+
+[[package]]
+name = "trust-dns-resolver"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10a3e6c3aff1718b3c73e395d1f35202ba2ffa847c6a62eea0db8fb4cfe30be6"
+dependencies = [
+ "cfg-if",
+ "futures-util",
+ "ipconfig",
+ "lru-cache",
+ "once_cell",
+ "parking_lot 0.12.3",
+ "rand",
+ "resolv-conf",
+ "smallvec",
+ "thiserror",
+ "tokio",
+ "tracing",
+ "trust-dns-proto 0.23.2",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
+name = "tt-call"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4f195fd851901624eee5a58c4bb2b4f06399148fcd0ed336e6f1cb60a9881df"
+
+[[package]]
+name = "tungstenite"
+version = "0.20.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9"
+dependencies = [
+ "byteorder",
+ "bytes",
+ "data-encoding",
+ "http 0.2.12",
+ "httparse",
+ "log",
+ "rand",
+ "rustls 0.21.12",
+ "sha1",
+ "thiserror",
+ "url",
+ "utf-8",
+]
+
+[[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.7",
+ "rand",
+ "static_assertions",
+]
+
+[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[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.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
+
+[[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.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
+
+[[package]]
+name = "unicode-width"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
+
+[[package]]
+name = "universal-hash"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
+dependencies = [
+ "crypto-common",
+ "subtle 2.6.1",
+]
+
+[[package]]
+name = "unsigned-varint"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105"
+dependencies = [
+ "asynchronous-codec",
+ "bytes",
+ "futures-io",
+ "futures-util",
+]
+
+[[package]]
+name = "unsigned-varint"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06"
+dependencies = [
+ "bytes",
+ "tokio-util",
+]
+
+[[package]]
+name = "untrusted"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "url"
+version = "2.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
+dependencies = [
+ "form_urlencoded",
+ "idna 1.0.3",
+ "percent-encoding",
+]
+
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
+[[package]]
+name = "utf16_iter"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[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.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "void"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+
+[[package]]
+name = "w3f-bls"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70a3028804c8bbae2a97a15b71ffc0e308c4b01a520994aafa77d56e94e19024"
+dependencies = [
+ "ark-bls12-377",
+ "ark-bls12-381",
+ "ark-ec",
+ "ark-ff",
+ "ark-serialize",
+ "ark-serialize-derive",
+ "arrayref",
+ "constcat",
+ "digest 0.10.7",
+ "rand",
+ "rand_chacha",
+ "rand_core",
+ "sha2 0.10.8",
+ "sha3",
+ "thiserror",
+ "zeroize",
+]
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "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.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
+dependencies = [
+ "bumpalo",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.50"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "once_cell",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "wasm-instrument"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a47ecb37b9734d1085eaa5ae1a81e60801fd8c28d4cabdd8aedb982021918bc"
+dependencies = [
+ "parity-wasm",
+]
+
+[[package]]
+name = "wasm-opt"
+version = "0.116.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fd87a4c135535ffed86123b6fb0f0a5a0bc89e50416c942c5f0662c645f679c"
+dependencies = [
+ "anyhow",
+ "libc",
+ "strum 0.24.1",
+ "strum_macros 0.24.3",
+ "tempfile",
+ "thiserror",
+ "wasm-opt-cxx-sys",
+ "wasm-opt-sys",
+]
+
+[[package]]
+name = "wasm-opt-cxx-sys"
+version = "0.116.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c57b28207aa724318fcec6575fe74803c23f6f266fce10cbc9f3f116762f12e"
+dependencies = [
+ "anyhow",
+ "cxx",
+ "cxx-build",
+ "wasm-opt-sys",
+]
+
+[[package]]
+name = "wasm-opt-sys"
+version = "0.116.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a1cce564dc768dacbdb718fc29df2dba80bd21cb47d8f77ae7e3d95ceb98cbe"
+dependencies = [
+ "anyhow",
+ "cc",
+ "cxx",
+ "cxx-build",
+]
+
+[[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 = "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 = "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",
+ "wasmtime-cache",
+ "wasmtime-cranelift",
+ "wasmtime-environ",
+ "wasmtime-jit",
+ "wasmtime-runtime",
+ "windows-sys 0.45.0",
+]
+
+[[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 = "8.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c86437fa68626fe896e5afc69234bb2b5894949083586535f200385adfd71213"
+dependencies = [
+ "anyhow",
+ "base64 0.21.7",
+ "bincode",
+ "directories-next",
+ "file-per-thread-logger",
+ "log",
+ "rustix 0.36.17",
+ "serde",
+ "sha2 0.10.8",
+ "toml 0.5.11",
+ "windows-sys 0.45.0",
+ "zstd 0.11.2+zstd.1.5.2",
+]
+
+[[package]]
+name = "wasmtime-cranelift"
+version = "8.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1cefde0cce8cb700b1b21b6298a3837dba46521affd7b8c38a9ee2c869eee04"
+dependencies = [
+ "anyhow",
+ "cranelift-codegen",
+ "cranelift-entity",
+ "cranelift-frontend",
+ "cranelift-native",
+ "cranelift-wasm",
+ "gimli 0.27.3",
+ "log",
+ "object 0.30.4",
+ "target-lexicon",
+ "thiserror",
+ "wasmparser",
+ "wasmtime-cranelift-shared",
+ "wasmtime-environ",
+]
+
+[[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",
+]
+
+[[package]]
+name = "wasmtime-environ"
+version = "8.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a990198cee4197423045235bf89d3359e69bd2ea031005f4c2d901125955c949"
+dependencies = [
+ "anyhow",
+ "cranelift-entity",
+ "gimli 0.27.3",
+ "indexmap 1.9.3",
+ "log",
+ "object 0.30.4",
+ "serde",
+ "target-lexicon",
+ "thiserror",
+ "wasmparser",
+ "wasmtime-types",
+]
+
+[[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",
+ "wasmtime-jit-debug",
+ "wasmtime-jit-icache-coherence",
+ "wasmtime-runtime",
+ "windows-sys 0.45.0",
+]
+
+[[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.17",
+]
+
+[[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 = "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",
+ "paste",
+ "rand",
+ "rustix 0.36.17",
+ "wasmtime-asm-macros",
+ "wasmtime-environ",
+ "wasmtime-jit-debug",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "wasmtime-types"
+version = "8.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f"
+dependencies = [
+ "cranelift-entity",
+ "serde",
+ "thiserror",
+ "wasmparser",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.77"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53"
+dependencies = [
+ "ring 0.17.8",
+ "untrusted 0.9.0",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "0.25.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
+
+[[package]]
+name = "which"
+version = "4.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
+dependencies = [
+ "either",
+ "home",
+ "once_cell",
+ "rustix 0.38.43",
+]
+
+[[package]]
+name = "wide"
+version = "0.7.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41b5576b9a81633f3e8df296ce0063042a73507636cbe956c61133dd7034ab22"
+dependencies = [
+ "bytemuck",
+ "safe_arch",
+]
+
+[[package]]
+name = "widestring"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
+
+[[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.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
+[[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.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538"
+dependencies = [
+ "windows-core 0.53.0",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.53.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dcc5b895a6377f1ab9fa55acedab1fd5ac0db66ad1e6c7f47e28a22e446a5dd"
+dependencies = [
+ "windows-result",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-result"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[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.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[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.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "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]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[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.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[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.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[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.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[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.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[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.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[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.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[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.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "winnow"
+version = "0.6.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a"
+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 = "write16"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
+
+[[package]]
+name = "writeable"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+
+[[package]]
+name = "wyz"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
+dependencies = [
+ "tap",
+]
+
+[[package]]
+name = "x25519-dalek"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
+dependencies = [
+ "curve25519-dalek",
+ "rand_core",
+ "serde",
+ "zeroize",
+]
+
+[[package]]
+name = "x509-parser"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7069fba5b66b9193bd2c5d3d4ff12b839118f6bcbef5328efafafb5395cf63da"
+dependencies = [
+ "asn1-rs 0.5.2",
+ "data-encoding",
+ "der-parser 8.2.0",
+ "lazy_static",
+ "nom",
+ "oid-registry 0.6.1",
+ "rusticata-macros",
+ "thiserror",
+ "time",
+]
+
+[[package]]
+name = "x509-parser"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69"
+dependencies = [
+ "asn1-rs 0.6.2",
+ "data-encoding",
+ "der-parser 9.0.0",
+ "lazy_static",
+ "nom",
+ "oid-registry 0.7.1",
+ "rusticata-macros",
+ "thiserror",
+ "time",
+]
+
+[[package]]
+name = "xml-rs"
+version = "0.8.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4"
+
+[[package]]
+name = "xmltree"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb"
+dependencies = [
+ "xml-rs",
+]
+
+[[package]]
+name = "yamux"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed0164ae619f2dc144909a9f082187ebb5893693d8c0196e8085283ccd4b776"
+dependencies = [
+ "futures",
+ "log",
+ "nohash-hasher",
+ "parking_lot 0.12.3",
+ "pin-project",
+ "rand",
+ "static_assertions",
+]
+
+[[package]]
+name = "yasna"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd"
+dependencies = [
+ "time",
+]
+
+[[package]]
+name = "yoke"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+ "synstructure 0.13.1",
+]
+
+[[package]]
+name = "zerocopy"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
+dependencies = [
+ "byteorder",
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+ "synstructure 0.13.1",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
+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.96",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.96",
+]
+
+[[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 5.0.2+zstd.1.5.2",
+]
+
+[[package]]
+name = "zstd"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c"
+dependencies = [
+ "zstd-safe 6.0.6",
+]
+
+[[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-safe"
+version = "6.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581"
+dependencies = [
+ "libc",
+ "zstd-sys",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "2.0.13+zstd.1.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa"
+dependencies = [
+ "cc",
+ "pkg-config",
+]
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..3574848
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,78 @@
+[profile.release]
+panic = "unwind"
+
+[workspace]
+members = [
+ "node",
+ "runtime",
+ "multi-pow",
+]
+resolver = "2"
+
+[workspace.dependencies]
+
+# Crates.io dependencies
+async-trait = { version = "0.1.53" }
+clap = { version = "4.5.3" }
+futures = { version = "0.3.30" }
+hex = "0.4"
+hex-literal = "0.4.1"
+jsonrpsee = { version = "0.23.2" }
+log = "0.4.25"
+md5 = "0.7.0"
+parity-scale-codec = { version = "3.1.2", features = [ "derive" ], default-features = false }
+rand = { version = "0.8.5", features = [ "small_rng" ] }
+scale-info = { version = "2.11.1", default-features = false }
+serde = "1.0.137"
+serde_json = { version = "1.0.114", default-features = false }
+sha3 = "0.10.1"
+
+# Local Dependencies
+academy-pow-runtime = { path = "../runtime" }
+multi-pow = { default-features = false, path = "../multi-pow" }
+
+# Polkadot SDK Dependencies
+frame-executive = { version = "37.0.0", default-features = false }
+frame-support = { version = "37.0.0", default-features = false }
+frame-system = { version = "37.0.0", default-features = false }
+frame-system-rpc-runtime-api = { version = "34.0.0", default-features = false }
+pallet-balances = { version = "38.0.0", default-features = false }
+pallet-timestamp = { version = "36.0.0", default-features = false }
+pallet-transaction-payment = { version = "37.0.0", default-features = false }
+pallet-transaction-payment-rpc = { version = "40.0.0" }
+pallet-transaction-payment-rpc-runtime-api = { version = "37.0.0", default-features = false }
+sc-basic-authorship = { version = "0.44.0" }
+sc-chain-spec = { version = "37.0.0" }
+sc-cli = { version = "0.46.0", default-features = false }
+sc-client-api = { version = "37.0.0" }
+sc-consensus = { version = "0.43.0" }
+sc-consensus-manual-seal = { version = "0.45.0" }
+sc-consensus-pow = { version = "0.43.0" }
+sc-executor = { version = "0.40.0" }
+sc-network = { version = "0.44.0" }
+sc-rpc-api = { version = "0.43.0" }
+sc-service = { version = "0.45.0", default-features = false }
+sc-telemetry = { version = "24.0.0" }
+sc-transaction-pool = { version = "37.0.0" }
+sc-transaction-pool-api = { version = "37.0.0" }
+sp-api = { version = "34.0.0", default-features = false }
+sp-block-builder = { version = "34.0.0", default-features = false }
+sp-blockchain = { version = "37.0.0" }
+sp-consensus = { version = "0.40.0" }
+sp-consensus-pow = { version = "0.40.0", default-features = false }
+sp-core = { version = "34.0.0", default-features = false }
+sp-genesis-builder = { version = "0.15.0", default-features = false }
+sp-inherents = { version = "34.0.0", default-features = false }
+sp-io = { version = "38.0.0", default-features = false }
+sp-keyring = { version = "39.0.0" }
+sp-keystore = { version = "0.40.0" }
+sp-offchain = { version = "34.0.0", default-features = false }
+sp-runtime = { version = "39.0.0", default-features = false }
+sp-session = { version = "35.0.0", default-features = false }
+sp-std = { version = "14.0.0", default-features = false }
+sp-timestamp = { version = "34.0.0" }
+sp-transaction-pool = { version = "34.0.0", default-features = false }
+sp-version = { version = "37.0.0", default-features = false }
+substrate-build-script-utils = { version = "11.0.0", default-features = false }
+substrate-frame-rpc-system = { version = "38.0.0" }
+substrate-wasm-builder = { version = "24.0.0", default-features = false }
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..cf1ab25
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org>
diff --git a/multi-pow/Cargo.toml b/multi-pow/Cargo.toml
new file mode 100644
index 0000000..0a3c115
--- /dev/null
+++ b/multi-pow/Cargo.toml
@@ -0,0 +1,29 @@
+[package]
+authors = [ "@danielbui12" ]
+description = "a concrete Substrate PoW algorithm that supports multiple hashing algorithms"
+edition = "2021"
+name = "multi-pow"
+version = "3.0.0"
+
+[dependencies]
+md5 = { workspace = true }
+parity-scale-codec = { workspace = true }
+scale-info = { workspace = true }
+sha3 = { workspace = true, optional = true }
+
+sc-client-api = { optional = true, workspace = true }
+sc-consensus-pow = { optional = true, workspace = true }
+sp-api = { workspace = true }
+sp-consensus-pow = { workspace = true }
+sp-core = { workspace = true }
+sp-runtime = { workspace = true }
+
+log = { workspace = true }
+
+[features]
+default = [ "std" ]
+std = [
+ "sha3",
+ "sc-consensus-pow",
+ "sc-client-api",
+]
diff --git a/multi-pow/src/lib.rs b/multi-pow/src/lib.rs
new file mode 100644
index 0000000..8c36e58
--- /dev/null
+++ b/multi-pow/src/lib.rs
@@ -0,0 +1,421 @@
+//! This crate represents a concrete Substrate PoW algorithm.
+//!
+//! It is multi-pow in the sense that there are multiple supported hashing algorithms.
+//! A seal with any of the supported hashing algorithms will be accepted.
+//!
+//! The purpose of this design is to demonstrate hard and soft forks by adding and removing valid hashing algorithms.
+//! While there is no precedent for changing hashing algorithms in the real world yet, it is conceivable that
+//! a chain may want to upgrade to a new algorithm when the old one is suspected weak.
+//! In any case, the point is that we want to demonstrate hard and soft forks in an understandable way,
+//! the multiple hashing algorithms achieves that well.
+//!
+//! In the future, the hope is that there will be a dedicated difficulty threshold for each hashing algorithm.
+//! But currently the Substrate PoW crates are not that flexible.
+//! We could solve it by adding a pre-digest that includes information about what hashing algo is being used
+//! for the runtime to use later in the difficulty adjustment.
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+use core::str::FromStr;
+#[cfg(feature = "std")]
+use std::sync::Arc;
+
+use parity_scale_codec::{Decode, Encode};
+#[cfg(feature = "std")]
+use sc_consensus_pow::{Error, PowAlgorithm};
+#[cfg(feature = "std")]
+use sha3::{Digest, Keccak256, Sha3_256};
+#[cfg(feature = "std")]
+use sp_api::ProvideRuntimeApi;
+#[cfg(feature = "std")]
+use sp_consensus_pow::DifficultyApi;
+#[cfg(feature = "std")]
+use sp_consensus_pow::Seal as RawSeal;
+use sp_consensus_pow::TotalDifficulty;
+use sp_core::{H256, U256};
+#[cfg(feature = "std")]
+use sp_runtime::generic::BlockId;
+#[cfg(feature = "std")]
+use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
+
+/// A struct that represents a difficulty threshold.
+/// Unlike a normal PoW algorithm this struct has a separate threshold for each hash
+#[derive(
+ Clone,
+ Copy,
+ PartialEq,
+ Eq,
+ PartialOrd,
+ Ord,
+ Encode,
+ Decode,
+ Debug,
+ Default,
+ scale_info::TypeInfo,
+)]
+pub struct Threshold {
+ pub md5: U256,
+ pub sha3: U256,
+ pub keccak: U256,
+}
+
+// This trait does not seem to be fully baked in the Substrate PoW code
+// But we do need some kind of sinsible impl here so the node can import blocks.
+// so I will not use it for now.
+impl TotalDifficulty for Threshold {
+ fn increment(&mut self, other: Threshold) {
+ self.md5 += other.md5;
+ self.sha3 += other.sha3;
+ self.keccak += other.keccak;
+ }
+}
+
+/// An enum that represents the supported hash types
+#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, Debug)]
+pub enum SupportedHashes {
+ Md5,
+ Sha3,
+ Keccak,
+}
+
+impl Default for SupportedHashes {
+ fn default() -> Self {
+ Self::Sha3
+ }
+}
+
+/// A struct that represents a concrete hash value tagged with what hashing
+/// algorithm was used to compute it.
+#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, Debug, Default)]
+pub struct MultiHash {
+ pub algo: SupportedHashes,
+ pub value: H256,
+}
+
+/// Determine whether the given hash satisfies the given difficulty.
+/// The test is done by multiplying the two together. If the product
+/// overflows the bounds of U256, then the product (and thus the hash)
+/// was too high.
+pub fn simple_hash_meets_difficulty(hash: &H256, difficulty: U256) -> bool {
+ let num_hash = U256::from_little_endian(&hash[..]);
+ let (_, overflowed) = num_hash.overflowing_mul(difficulty);
+
+ !overflowed
+}
+
+pub fn multi_hash_meets_difficulty(hash: &MultiHash, difficulty: Threshold) -> bool {
+ match hash.algo {
+ SupportedHashes::Md5 => simple_hash_meets_difficulty(&hash.value, difficulty.md5),
+ SupportedHashes::Sha3 => simple_hash_meets_difficulty(&hash.value, difficulty.sha3),
+ SupportedHashes::Keccak => simple_hash_meets_difficulty(&hash.value, difficulty.keccak),
+ }
+}
+
+/// A Seal struct that will be encoded to a Vec<u8> as used as the
+/// `RawSeal` type.
+#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)]
+pub struct Seal {
+ pub work: MultiHash,
+ pub difficulty: Threshold,
+ pub nonce: U256,
+}
+
+/// A not-yet-computed attempt to solve the proof of work. Calling the
+/// compute method will compute the hash and return the seal.
+#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)]
+pub struct Compute {
+ pub difficulty: Threshold,
+ pub pre_hash: H256,
+ pub nonce: U256,
+}
+
+#[cfg(feature = "std")]
+impl Compute {
+ pub fn compute(self, algo: SupportedHashes) -> Seal {
+ let value = match algo {
+ SupportedHashes::Md5 => {
+ // The md5 is only 16 byte output, so we just concatenate it twice to
+ // get an H256
+ let bytes = *md5::compute(&self.encode()[..]);
+ let mut doubled = [0u8; 32];
+ doubled[0..16].copy_from_slice(&bytes[0..16]);
+ doubled[16..32].copy_from_slice(&bytes[0..16]);
+
+ H256::from(doubled)
+ }
+ SupportedHashes::Sha3 => {
+ H256::from_slice(Sha3_256::digest(&self.encode()[..]).as_slice())
+ }
+ SupportedHashes::Keccak => {
+ H256::from_slice(Keccak256::digest(&self.encode()[..]).as_slice())
+ }
+ };
+
+ Seal {
+ nonce: self.nonce,
+ difficulty: self.difficulty,
+ work: MultiHash { algo, value },
+ }
+ }
+}
+
+#[cfg(feature = "std")]
+/// A complete PoW Algorithm that uses multiple hashing algorithms.
+/// Needs a reference to the client so it can grab the difficulty from the runtime.
+pub struct MultiPow<C> {
+ client: Arc<C>,
+ fork_config: ForkingConfig,
+}
+
+#[cfg(feature = "std")]
+impl<C> MultiPow<C> {
+ pub fn new(client: Arc<C>, fork_config: ForkingConfig) -> Self {
+ Self {
+ client,
+ fork_config,
+ }
+ }
+}
+
+//TODO could maybe derive clone_no_bound
+#[cfg(feature = "std")]
+impl<C> Clone for MultiPow<C> {
+ fn clone(&self) -> Self {
+ Self::new(self.client.clone(), self.fork_config)
+ }
+}
+
+// Here we implement the general PowAlgorithm trait for our concrete algorithm.
+#[cfg(feature = "std")]
+impl<B: BlockT<Hash = H256>, C> PowAlgorithm<B> for MultiPow<C>
+where
+ C: ProvideRuntimeApi<B>,
+ C::Api: DifficultyApi<B, Threshold>,
+ C: sc_client_api::HeaderBackend<B>,
+{
+ type Difficulty = Threshold;
+
+ fn difficulty(&self, parent: B::Hash) -> Result<Self::Difficulty, Error<B>> {
+ let difficulty = self
+ .client
+ .runtime_api()
+ .difficulty(parent)
+ .map_err(|err| {
+ sc_consensus_pow::Error::Environment(format!(
+ "Fetching difficulty from runtime failed: {:?}",
+ err
+ ))
+ })?;
+
+ Ok(difficulty)
+ }
+
+ fn verify(
+ &self,
+ parent_id: &BlockId<B>,
+ pre_hash: &H256,
+ pre_digest: Option<&[u8]>,
+ seal: &RawSeal,
+ difficulty: Self::Difficulty,
+ ) -> Result<bool, Error<B>> {
+ // Try to construct a seal object by decoding the raw seal given
+ let seal = match Seal::decode(&mut &seal[..]) {
+ Ok(seal) => seal,
+ Err(_) => return Ok(false),
+ };
+
+ log::debug!("✅ Decode seal passed!");
+
+ let Some(_encoded_pre_digest) = pre_digest else {
+ return Ok(false);
+ };
+
+ log::debug!("✅ Checksum digest passed!");
+
+ // // TODO idk why this always return md5 only
+ // let algo_from_predigest = match SupportedHashes::decode(&mut &encoded_pre_digest[..]) {
+ // Ok(algo) => algo,
+ // Err(_) => return Ok(false),
+ // };
+
+ // log::debug!("✅ Get algorithm from digest passed!");
+
+ // // Check that the pre-digest algo matches the seal algo
+ // // TODO it shouldn't be necessary to have both.
+ // if seal.work.algo != algo_from_predigest {
+ // return Ok(false);
+ // }
+
+ // log::debug!("✅ Checksum algorithm from seal passed!");
+
+ // This is where we handle forks on the verification side.
+ // We will still need to handle it in the mining algorithm somewhere.
+ // Currently we make the miner configure what algo they mine manually with their cli.
+ let parent_number: u32 = match parent_id {
+ BlockId::Hash(h) => *self
+ .client
+ .header(*h)
+ .expect("Database should perform lookup successfully")
+ .expect("parent header should be present in the db")
+ .number(),
+ BlockId::Number(n) => *n,
+ }
+ .try_into()
+ .map_err(|_| ())
+ .expect("Block numbers can be converted to u32 (because they are u32)");
+
+ log::debug!("✅ Checksum parent block number passed!");
+
+ // Here we handle the forking logic according the the node operator's request.
+ let valid_algorithm = match self.fork_config {
+ ForkingConfig::Manual => manual_fork_validation(parent_number, seal.work.algo),
+ ForkingConfig::Automatic(fork_heights, maxi_position) => {
+ auto_fork_validation(parent_number, seal.work.algo, fork_heights, maxi_position)
+ }
+ };
+
+ if !valid_algorithm {
+ return Ok(false);
+ }
+
+ log::debug!("✅ Valid algorithm!");
+
+ // See whether the hash meets the difficulty requirement. If not, fail fast.
+ if !multi_hash_meets_difficulty(&seal.work, difficulty) {
+ return Ok(false);
+ }
+
+ log::debug!("✅ Checksum difficulty passed!");
+
+ // Make sure the provided work actually comes from the correct pre_hash
+ let compute = Compute {
+ difficulty,
+ pre_hash: *pre_hash,
+ nonce: seal.nonce,
+ };
+
+ if compute.compute(seal.work.algo) != seal {
+ return Ok(false);
+ }
+
+ log::debug!("✅ Re-compute passed!");
+
+ log::debug!("🛠️ All passed, append the block to the chain ...");
+
+ Ok(true)
+ }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+///
+pub struct ForkHeights {
+ /// The block height to perform the soft fork that adds sha3 and keccak support.
+ pub add_sha3_keccak: u32,
+ /// The block height to perform the hard fork that removes md5 support.
+ pub remove_md5: u32,
+ /// The block height to perform the contentious fork where some become sha3- or keccak-maxis.
+ pub split_sha3_keccak: u32,
+}
+
+/// Various political positions a node could take when the network is forking into
+/// keccak maxis and sha3 maxis
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum MaxiPosition {
+ /// Allow all blocks, both sha3 and keccak
+ NoMaxi,
+ /// Only allow sha3 blocks
+ Sha3Maxi,
+ /// Only allow keccak blocks
+ KeccakMaxi,
+ /// Only allow a single type of blocks. Which type it is is determined by what algo the node is mining.
+ FollowMining,
+}
+
+#[derive(Copy, Clone, Eq, PartialEq)]
+/// The actual properly typed config after we're done working around all the BS.
+pub enum ForkingConfig {
+ ///
+ Manual,
+ ///
+ Automatic(ForkHeights, MaxiPosition),
+}
+
+impl FromStr for MaxiPosition {
+ type Err = ();
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ Ok(match &s.to_lowercase()[..] {
+ "allow-all" | "allowall" | "no-maxi" | "nomaxi" => Self::NoMaxi,
+ "sha3-maxi" | "sha3maxi" => Self::Sha3Maxi,
+ "keccak-maxi" | "keccakmaxi" => Self::KeccakMaxi,
+ _ => Self::FollowMining,
+ })
+ }
+}
+
+/// Manual mode, the node operator manually specifies which hashing algorithms are valid through the mining client.
+/// If you would like to do a fork, simply allow, un-allow some algorithms to check it.
+fn manual_fork_validation(_parent_number: u32, algo: SupportedHashes) -> bool {
+ use SupportedHashes::*;
+
+ // To begin with, allow all algorithms.
+ // After the fork height this check is skipped so all the hashes become valid.
+ match algo {
+ Md5 => true,
+ Sha3 => true,
+ Keccak => true,
+ }
+}
+
+/// In automatic mode, the `ForkHeights` and `MaxiPosition` structs define the forking schedule
+/// and the node's behavior during the contentious fork
+/// (where the network splits into two chains supporting different hashing algorithms).
+/// The validation logic considers the parent block height,
+/// forking configuration parameters, and the hashing algorithm used in the PoW solution to determine its validity.
+fn auto_fork_validation(
+ parent_number: u32,
+ algo: SupportedHashes,
+ fork_heights: ForkHeights,
+ maxi_position: MaxiPosition,
+) -> bool {
+ use MaxiPosition::*;
+ use SupportedHashes::*;
+
+ log::debug!("parent_number: {:?}", parent_number);
+ log::debug!("fork_heights: {:?}", fork_heights);
+
+ if parent_number < fork_heights.add_sha3_keccak {
+ // To begin with we only allow md5 hashes for our pow.
+ // After the fork height this check is skipped so all the hashes become valid.
+ match algo {
+ Md5 => true,
+ Sha3 => false,
+ Keccak => false,
+ }
+ } else if parent_number < fork_heights.remove_md5 {
+ // After the first fork, all three algos become valid.
+ match algo {
+ Md5 => true,
+ Sha3 => true,
+ Keccak => true,
+ }
+ } else if parent_number < fork_heights.split_sha3_keccak {
+ // After the second fork, md5 is no longer valid.
+ match algo {
+ Md5 => false,
+ Sha3 => true,
+ Keccak => true,
+ }
+ } else {
+ // Finally we have the contentious fork.
+ // Our behavior here depends which maxi position we have taken.
+ #[allow(clippy::match_like_matches_macro)]
+ match (algo, maxi_position) {
+ (Sha3, Sha3Maxi) => true,
+ (Sha3, NoMaxi) => true,
+ (Keccak, KeccakMaxi) => true,
+ (Keccak, NoMaxi) => true,
+ _ => false,
+ }
+ }
+}
diff --git a/node/Cargo.toml b/node/Cargo.toml
new file mode 100644
index 0000000..6ace2b0
--- /dev/null
+++ b/node/Cargo.toml
@@ -0,0 +1,55 @@
+[package]
+authors = [ "@danielbui12" ]
+build = "build.rs"
+edition = "2021"
+name = "academy-pow"
+version = "3.0.0"
+
+[dependencies]
+clap = { workspace = true }
+futures = { workspace = true }
+hex = { workspace = true }
+log = { workspace = true }
+parity-scale-codec = { workspace = true }
+rand = { workspace = true }
+serde_json = { workspace = true }
+serde = { workspace = true }
+sha3 = { workspace = true }
+jsonrpsee = { workspace = true }
+
+# Local Dependencies
+academy-pow-runtime = { path = "../runtime" }
+multi-pow = { path = "../multi-pow" }
+
+# Polkadot SDK
+pallet-transaction-payment-rpc = { workspace = true }
+sc-rpc-api = { workspace = true }
+sp-block-builder = { workspace = true }
+substrate-frame-rpc-system = { workspace = true }
+sc-basic-authorship = { workspace = true }
+sc-chain-spec = { workspace = true }
+sc-cli = { workspace = true }
+sc-client-api = { workspace = true }
+sc-consensus = { workspace = true }
+sc-consensus-manual-seal = { workspace = true }
+sc-consensus-pow = { workspace = true }
+sc-executor = { workspace = true }
+sc-network = { workspace = true }
+sc-service = { workspace = true }
+sc-telemetry = { workspace = true }
+sc-transaction-pool = { workspace = true }
+sc-transaction-pool-api = { workspace = true }
+sp-api = { workspace = true }
+sp-blockchain = { workspace = true }
+sp-consensus = { workspace = true }
+sp-consensus-pow = { workspace = true }
+sp-core = { workspace = true }
+sp-inherents = { workspace = true }
+sp-io = { workspace = true, default-features = true}
+sp-keyring = { workspace = true }
+sp-runtime = { workspace = true }
+sp-timestamp = { workspace = true }
+sp-transaction-pool = { workspace = true }
+
+[build-dependencies]
+substrate-build-script-utils = { workspace = true }
diff --git a/node/build.rs b/node/build.rs
new file mode 100644
index 0000000..f97fd98
--- /dev/null
+++ b/node/build.rs
@@ -0,0 +1,4 @@
+fn main() {
+ substrate_build_script_utils::generate_cargo_keys();
+ substrate_build_script_utils::rerun_if_git_head_changed();
+}
diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs
new file mode 100644
index 0000000..a8e1732
--- /dev/null
+++ b/node/src/chain_spec.rs
@@ -0,0 +1,188 @@
+use std::str::FromStr;
+
+use academy_pow_runtime::{
+ AccountId,
+ SS58Prefix,
+ Signature,
+ TOKEN_DECIMALS,
+ TOKEN_SYMBOL,
+ WASM_BINARY,
+};
+use multi_pow::{ForkHeights, ForkingConfig, MaxiPosition};
+use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup};
+use sc_service::ChainType;
+use serde::{Deserialize, Serialize};
+use sp_core::{sr25519, Pair, Public};
+use sp_core::{ByteArray, H256};
+use sp_runtime::traits::{IdentifyAccount, Verify};
+
+/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type.
+pub type ChainSpec = sc_service::GenericChainSpec<ForkingExtensions>;
+
+/// PoW and Forking related chain spec extensions to configure the client side forking behavior.
+///
+/// The forks here are all related to adding and removing hash algorithms from the PoW.
+/// The chain begins supporting only md5. Later is adds sha3 and keccak. Later it removes md5.
+/// And finally there is a contentious fork where people become maxis.
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)]
+#[serde(deny_unknown_fields)]
+pub struct ForkingExtensions {
+ /// Manual mode is intended for when we you are running a live workshop.
+ /// No forking happens automatically. Rather, you have to hard-code the forks.
+ ///
+ /// If manual mode is enabled, the rest of the parameters are ignored.
+ /// This should really be an enum, but I have to work around the broken extension system.
+ ///
+ /// Aww damn it! I can't even use bool in this broken system? Okay then I guess 0 means automatic mode
+ /// and anything else means manual mode.
+ pub manual_mode: u32,
+ /// The block height to perform the soft fork that adds sha3 and keccak support.
+ pub add_sha3_keccak: u32,
+ /// The block height to perform the hard fork that removes md5 support.
+ pub remove_md5: u32,
+ /// The block height to perform the contentious fork where some become sha3- or keccak-maxis.
+ pub split_sha3_keccak: u32,
+ // Damn extension thing is so fragile, I can't even use an enum here.
+ // Let alone that time I tried to use the forked value feature.
+ /// The political position that this node will take at the contentious fork.
+ pub maxi_position: String,
+}
+
+impl From<&ForkingExtensions> for ForkingConfig {
+ fn from(e: &ForkingExtensions) -> Self {
+ if e.manual_mode > 0 {
+ return Self::Manual;
+ }
+
+ let fork_heights = ForkHeights {
+ add_sha3_keccak: e.add_sha3_keccak,
+ remove_md5: e.remove_md5,
+ split_sha3_keccak: e.split_sha3_keccak,
+ };
+
+ let maxi_position =
+ MaxiPosition::from_str(&e.maxi_position).expect("Should have a valid maxi position...");
+
+ Self::Automatic(fork_heights, maxi_position)
+ }
+}
+
+impl ForkingExtensions {
+ /// Try to get the extension from the given `ChainSpec`.
+ pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> {
+ sc_chain_spec::get_extension(chain_spec.extensions())
+ }
+}
+
+/// Generate a crypto pair from seed.
+pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
+ TPublic::Pair::from_string(&format!("//{}", seed), None)
+ .expect("static values are valid; qed")
+ .public()
+}
+
+type AccountPublic = <Signature as Verify>::Signer;
+
+/// Generate an account ID from seed.
+pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId
+where
+ AccountPublic: From<<TPublic::Pair as Pair>::Public>,
+{
+ AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
+}
+
+pub fn development_config() -> Result<ChainSpec, String> {
+ Ok(ChainSpec::builder(
+ WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?,
+ ForkingExtensions {
+ manual_mode: 1, // change this to `0` if you want to try `auto_fork_validation`
+ add_sha3_keccak: 10,
+ remove_md5: 20,
+ split_sha3_keccak: 30,
+ maxi_position: String::from("follow-mining"),
+ },
+ )
+ .with_name("Development")
+ .with_id("dev")
+ .with_chain_type(ChainType::Development)
+ .with_genesis_config_patch(genesis(
+ // Pre-funded accounts
+ vec![
+ get_account_id_from_seed::<sr25519::Public>("Alice"),
+ get_account_id_from_seed::<sr25519::Public>("Bob"),
+ get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
+ get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
+ ],
+ // Initial Difficulty
+ 4_000_000,
+ ))
+ .with_properties(system_properties())
+ .build())
+}
+
+pub fn testnet_config() -> Result<ChainSpec, String> {
+ Ok(ChainSpec::builder(
+ WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?,
+ ForkingExtensions {
+ manual_mode: 1,
+ add_sha3_keccak: 0,
+ remove_md5: 0,
+ split_sha3_keccak: 0,
+ maxi_position: String::new(),
+ },
+ )
+ .with_name("Testnet")
+ .with_id("testnet")
+ .with_chain_type(ChainType::Local)
+ .with_genesis_config_patch(genesis(
+ vec![
+ get_account_id_from_seed::<sr25519::Public>("Alice"),
+ get_account_id_from_seed::<sr25519::Public>("Bob"),
+ get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
+ get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
+ ],
+ 4_000_000,
+ ))
+ .with_properties(system_properties())
+ .build())
+}
+
+fn genesis(
+ endowed_accounts: Vec<AccountId>,
+ initial_difficulty: u32,
+) -> serde_json::Value {
+ serde_json::json!({
+ "balances": {
+ // Configure endowed accounts with initial balance of 1 << 50.
+ "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 50)).collect::<Vec<_>>(),
+ },
+ "keccakDifficultyAdjustment": {
+ "initialDifficulty": u32_to_u8_32(initial_difficulty),
+ },
+ "md5DifficultyAdjustment": {
+ "initialDifficulty": u32_to_u8_32(initial_difficulty),
+ },
+ "sha3DifficultyAdjustment": {
+ "initialDifficulty": u32_to_u8_32(initial_difficulty),
+ },
+ })
+}
+
+/// Convert u32 (default value) to [u8;32] (U256)
+/// in little-endian format
+pub fn u32_to_u8_32(num: u32) -> [u8; 32] {
+ let mut result = [0u8; 32];
+ let bytes = num.to_le_bytes();
+ result[..4].copy_from_slice(&bytes);
+ result
+}
+
+fn system_properties() -> sc_chain_spec::Properties {
+ let mut properties = sc_chain_spec::Properties::new();
+
+ properties.insert("ss58Format".into(), SS58Prefix::get().into());
+ properties.insert("tokenSymbol".into(), TOKEN_SYMBOL.into());
+ properties.insert("tokenDecimals".into(), TOKEN_DECIMALS.into());
+
+ properties
+}
diff --git a/node/src/cli.rs b/node/src/cli.rs
new file mode 100644
index 0000000..4e1ac61
--- /dev/null
+++ b/node/src/cli.rs
@@ -0,0 +1,148 @@
+use academy_pow_runtime::AccountId;
+use multi_pow::SupportedHashes;
+use sc_cli::{
+ clap::{ArgGroup, Parser},
+ RunCmd,
+};
+use sc_service::ChainType;
+use sp_core::{crypto::Ss58Codec, sr25519};
+
+#[derive(Debug, Parser)]
+#[clap(subcommand_negates_reqs(true), version(env!("SUBSTRATE_CLI_IMPL_VERSION")))]
+pub struct Cli {
+ #[clap(subcommand)]
+ pub subcommand: Option<Subcommand>,
+
+ #[command(flatten)]
+ pub pow: AcademyPowCli,
+
+ #[clap(flatten)]
+ pub run: RunCmd,
+}
+
+#[derive(Debug, Parser, Clone)]
+#[clap(group(ArgGroup::new("backup")))]
+pub struct AcademyPowCli {
+ /// Miner's AccountId (base58 encoding of an SR25519 public key) for the block rewards
+ #[clap(long,
+ conflicts_with = "mining_public_key",
+ value_parser = parse_account_id)]
+ pub mining_account_id: Option<AccountId>,
+
+ /// Miner's hex encoding of the SR25519 public key) for the block rewards
+ #[clap(
+ long,
+ conflicts_with = "mining_account_id",
+ value_parser = parse_sr25519_public_key
+ )]
+ pub mining_public_key: Option<sr25519::Public>,
+
+ /// The mining algorithm to use
+ #[clap(long, value_parser = parse_algo, default_value = "md5")]
+ pub mining_algo: multi_pow::SupportedHashes,
+
+ /// whether to use instant seal
+ #[clap(long, default_value = "false")]
+ pub instant_seal: bool,
+}
+
+impl AcademyPowCli {
+ pub fn public_key_bytes(&self, keyring: Option<sp_keyring::Sr25519Keyring>) -> [u8; 32] {
+ match &self.mining_account_id {
+ Some(account_id) => *account_id.as_ref(),
+ None => match self.mining_public_key {
+ Some(public_key) => public_key.0,
+ None => keyring.map(|k| k.to_raw_public()).unwrap_or([0u8; 32]),
+ },
+ }
+ }
+}
+
+#[derive(Debug, Parser)]
+pub struct BuildSpecCmd {
+ #[clap(flatten)]
+ pub base: sc_cli::BuildSpecCmd,
+
+ /// Chain name.
+ #[arg(long, default_value = "Academy PoW")]
+ pub chain_name: String,
+
+ /// Chain ID is a short identifier of the chain
+ #[arg(long, value_name = "ID", default_value = "academy_pow")]
+ pub chain_id: String,
+
+ /// AccountIds of the optional rich accounts
+ #[arg(long, value_delimiter = ',', value_parser = parse_account_id, num_args=1..)]
+ pub endowed_accounts: Option<Vec<AccountId>>,
+
+ /// The type of the chain. Possible values: "dev", "local", "live" (default)
+ #[arg(long, value_name = "TYPE", value_parser = parse_chaintype, default_value = "live")]
+ pub chain_type: ChainType,
+
+ #[arg(long, default_value = "4000000")]
+ pub initial_difficulty: u32,
+}
+
+fn parse_algo(s: &str) -> Result<SupportedHashes, String> {
+ Ok(match s {
+ "md" | "Md" | "md5" | "Md5" => SupportedHashes::Md5,
+ "sha" | "sha3" | "Sha" | "Sha3" => SupportedHashes::Sha3,
+ "keccak" | "Keccak" => SupportedHashes::Keccak,
+ s => panic!(
+ "Wrong mining algo: {}. Possible values: md5, sha3, keccak",
+ s
+ ),
+ })
+}
+
+fn parse_chaintype(s: &str) -> Result<ChainType, String> {
+ Ok(match s {
+ "dev" => ChainType::Development,
+ "local" => ChainType::Local,
+ "live" => ChainType::Live,
+ s => panic!("Wrong chain type {} Possible values: dev local live", s),
+ })
+}
+
+/// Parse AccountId from a string argument passed on the command line.
+fn parse_account_id(s: &str) -> Result<AccountId, String> {
+ Ok(AccountId::from_string(s)
+ .expect("Passed string is not a bas58 encoding of a sr25519 public key"))
+}
+
+/// Parse sr25519 pubkey from a string argument passed on the command line.
+fn parse_sr25519_public_key(s: &str) -> Result<sr25519::Public, String> {
+ Ok(sr25519::Public::from_string(s)
+ .expect("Passed string is not a hex encoding of a sr25519 public key"))
+}
+
+#[derive(Debug, clap::Subcommand)]
+pub enum Subcommand {
+ /// Key management cli utilities
+ #[command(subcommand)]
+ Key(sc_cli::KeySubcommand),
+
+ /// Build a chain specification.
+ BuildSpec(BuildSpecCmd),
+
+ /// Validate blocks.
+ CheckBlock(sc_cli::CheckBlockCmd),
+
+ /// Export blocks.
+ ExportBlocks(sc_cli::ExportBlocksCmd),
+
+ /// Export the state of a given block into a chain spec.
+ ExportState(sc_cli::ExportStateCmd),
+
+ /// Import blocks.
+ ImportBlocks(sc_cli::ImportBlocksCmd),
+
+ /// Remove the whole chain.
+ PurgeChain(sc_cli::PurgeChainCmd),
+
+ /// Revert the chain to a previous state.
+ Revert(sc_cli::RevertCmd),
+
+ /// Db meta columns information.
+ ChainInfo(sc_cli::ChainInfoCmd),
+}
diff --git a/node/src/command.rs b/node/src/command.rs
new file mode 100644
index 0000000..b62bd98
--- /dev/null
+++ b/node/src/command.rs
@@ -0,0 +1,183 @@
+// Copyright 2017-2020 Parity Technologies (UK) Ltd.
+// This file is part of Substrate.
+
+// Substrate is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Substrate is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
+
+use academy_pow_runtime::Block;
+use multi_pow::{ForkingConfig, MaxiPosition};
+use sc_cli::SubstrateCli;
+use sc_service::PartialComponents;
+use sp_core::sr25519;
+
+use crate::{
+ chain_spec::{self, ForkingExtensions},
+ cli::{Cli, Subcommand},
+ service,
+};
+
+impl SubstrateCli for Cli {
+ fn impl_name() -> String {
+ "Academy PoW Chain".into()
+ }
+
+ fn impl_version() -> String {
+ env!("SUBSTRATE_CLI_IMPL_VERSION").into()
+ }
+
+ fn executable_name() -> String {
+ env!("CARGO_PKG_NAME").into()
+ }
+
+ fn author() -> String {
+ env!("CARGO_PKG_AUTHORS").into()
+ }
+
+ fn description() -> String {
+ env!("CARGO_PKG_DESCRIPTION").into()
+ }
+
+ fn support_url() -> String {
+ "https://github.com/danielbui12/substrate-bitcoin-like-blockchain/issues/new".into()
+ }
+
+ fn copyright_start_year() -> i32 {
+ 2025
+ }
+
+ fn load_spec(&self, id: &str) -> Result<Box<dyn sc_service::ChainSpec>, String> {
+ Ok(match id {
+ "" => Box::new(chain_spec::ChainSpec::from_json_bytes(
+ &include_bytes!("../../spec.json")[..],
+ )?),
+ "dev" => Box::new(chain_spec::development_config()?),
+ "local" => Box::new(chain_spec::testnet_config()?),
+ path => Box::new(chain_spec::ChainSpec::from_json_file(
+ std::path::PathBuf::from(path),
+ )?),
+ })
+ }
+}
+
+/// Parse and run command line arguments
+pub fn run() -> sc_cli::Result<()> {
+ let cli = Cli::from_args();
+
+ match &cli.subcommand {
+ Some(Subcommand::Key(cmd)) => cmd.run(&cli),
+ Some(Subcommand::BuildSpec(cmd)) => {
+ let runner = cli.create_runner(&cmd.base)?;
+ runner.sync_run(|config| cmd.base.run(config.chain_spec, config.network))
+ }
+ Some(Subcommand::CheckBlock(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+ runner.async_run(|config| {
+ let PartialComponents {
+ client,
+ task_manager,
+ import_queue,
+ ..
+ } = service::new_partial(&config, ForkingConfig::Manual)?;
+ Ok((cmd.run(client, import_queue), task_manager))
+ })
+ }
+ Some(Subcommand::ExportBlocks(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+ runner.async_run(|config| {
+ let PartialComponents {
+ client,
+ task_manager,
+ ..
+ } = service::new_partial(&config, ForkingConfig::Manual)?;
+ Ok((cmd.run(client, config.database), task_manager))
+ })
+ }
+ Some(Subcommand::ExportState(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+ runner.async_run(|config| {
+ let PartialComponents {
+ client,
+ task_manager,
+ ..
+ } = service::new_partial(&config, ForkingConfig::Manual)?;
+ Ok((cmd.run(client, config.chain_spec), task_manager))
+ })
+ }
+ Some(Subcommand::ImportBlocks(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+ runner.async_run(|config| {
+ let PartialComponents {
+ client,
+ task_manager,
+ import_queue,
+ ..
+ } = service::new_partial(&config, ForkingConfig::Manual)?;
+ Ok((cmd.run(client, import_queue), task_manager))
+ })
+ }
+ Some(Subcommand::PurgeChain(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+ runner.sync_run(|config| cmd.run(config.database))
+ }
+ Some(Subcommand::Revert(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+ runner.async_run(|config| {
+ let PartialComponents {
+ client,
+ task_manager,
+ backend,
+ ..
+ } = service::new_partial(&config, ForkingConfig::Manual)?;
+ Ok((cmd.run(client, backend, None), task_manager))
+ })
+ }
+ Some(Subcommand::ChainInfo(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+ runner.sync_run(|config| cmd.run::<Block>(&config))
+ }
+ None => {
+ // Get the mining account from the cli
+ let bytes: [u8; 32] = cli.pow.public_key_bytes(cli.run.get_keyring());
+ let sr25519_public_key = sr25519::Public::from_raw(bytes);
+
+ let runner = cli.create_runner(&cli.run)?;
+ runner.run_node_until_exit(|config| async move {
+ // Get the forking information from the chain spec extension.
+ // Convert it to a strong type, and fill in the proper maxi position if they are following mining.
+ let forking_extension = ForkingExtensions::try_get(&*config.chain_spec)
+ .expect("Should be able to get the fork config from the extension");
+ let forking_config = match ForkingConfig::from(forking_extension) {
+ ForkingConfig::Automatic(fork_heights, MaxiPosition::FollowMining) => {
+ let maxi_position = match cli.pow.mining_algo {
+ multi_pow::SupportedHashes::Md5 => MaxiPosition::NoMaxi,
+ multi_pow::SupportedHashes::Sha3 => MaxiPosition::Sha3Maxi,
+ multi_pow::SupportedHashes::Keccak => MaxiPosition::KeccakMaxi,
+ };
+ ForkingConfig::Automatic(fork_heights, maxi_position)
+ }
+ old_config => old_config,
+ };
+
+ service::new_full::<sc_network::Litep2pNetworkBackend>(
+ config,
+ forking_config,
+ //TODO Combine the following three fields into a MiningConfig analogous to the ForkingConfig
+ sr25519_public_key,
+ cli.pow.instant_seal,
+ cli.pow.mining_algo,
+ )
+ .map_err(sc_cli::Error::Service)
+ })
+ }
+ }
+}
diff --git a/node/src/main.rs b/node/src/main.rs
new file mode 100644
index 0000000..a4182cd
--- /dev/null
+++ b/node/src/main.rs
@@ -0,0 +1,13 @@
+//! Substrate Node Template CLI library.
+#![warn(missing_docs)]
+
+mod chain_spec;
+#[macro_use]
+mod service;
+mod cli;
+mod command;
+mod rpc;
+
+fn main() -> sc_cli::Result<()> {
+ command::run()
+}
diff --git a/node/src/rpc.rs b/node/src/rpc.rs
new file mode 100644
index 0000000..875414c
--- /dev/null
+++ b/node/src/rpc.rs
@@ -0,0 +1,55 @@
+//! A collection of node-specific RPC methods.
+//! Substrate provides the `sc-rpc` crate, which defines the core RPC layer
+//! used by Substrate nodes. This file extends those RPC definitions with
+//! capabilities that are specific to this project's runtime configuration.
+
+#![warn(missing_docs)]
+
+use std::sync::Arc;
+
+use academy_pow_runtime::{opaque::Block, AccountId, Balance, Index};
+use jsonrpsee::RpcModule;
+pub use sc_rpc_api::DenyUnsafe;
+use sc_transaction_pool_api::TransactionPool;
+use sp_api::ProvideRuntimeApi;
+use sp_block_builder::BlockBuilder;
+use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata};
+
+/// Full client dependencies.
+pub struct FullDeps<C, P> {
+ /// The client instance to use.
+ pub client: Arc<C>,
+ /// Transaction pool instance.
+ pub pool: Arc<P>,
+ /// Whether to deny unsafe calls
+ pub deny_unsafe: DenyUnsafe,
+}
+
+/// Instantiate all full RPC extensions.
+pub fn create_full<C, P>(
+ deps: FullDeps<C, P>,
+) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
+where
+ C: ProvideRuntimeApi<Block>,
+ C: HeaderBackend<Block> + HeaderMetadata<Block, Error = BlockChainError> + 'static,
+ C: Send + Sync + 'static,
+ C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>,
+ C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>,
+ C::Api: BlockBuilder<Block>,
+ P: TransactionPool + 'static,
+{
+ use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer};
+ use substrate_frame_rpc_system::{System, SystemApiServer};
+
+ let mut module = RpcModule::new(());
+ let FullDeps {
+ client,
+ pool,
+ deny_unsafe,
+ } = deps;
+
+ module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?;
+ module.merge(TransactionPayment::new(client).into_rpc())?;
+
+ Ok(module)
+}
diff --git a/node/src/service.rs b/node/src/service.rs
new file mode 100644
index 0000000..4bf3935
--- /dev/null
+++ b/node/src/service.rs
@@ -0,0 +1,288 @@
+//! Service and ServiceFactory implementation. Specialized wrapper over substrate service.
+
+use core::clone::Clone;
+use std::sync::Arc;
+
+use academy_pow_runtime::{self, opaque::Block, PreDigest, RuntimeApi};
+use multi_pow::{ForkingConfig, MultiPow, SupportedHashes};
+use parity_scale_codec::Encode;
+use sc_consensus::LongestChain;
+use sc_service::{error::Error as ServiceError, Configuration, PartialComponents, TaskManager};
+use sc_telemetry::{Telemetry, TelemetryWorker};
+use sp_core::sr25519;
+
+pub(crate) type FullClient = sc_service::TFullClient<
+ Block,
+ RuntimeApi,
+ sc_executor::WasmExecutor<sp_io::SubstrateHostFunctions>,
+>;
+type FullBackend = sc_service::TFullBackend<Block>;
+type FullSelectChain = sc_consensus::LongestChain<FullBackend, Block>;
+
+type BasicImportQueue = sc_consensus::DefaultImportQueue<Block>;
+type BoxBlockImport = sc_consensus::BoxBlockImport<Block>;
+
+pub type Service = PartialComponents<
+ FullClient,
+ FullBackend,
+ FullSelectChain,
+ BasicImportQueue,
+ sc_transaction_pool::FullPool<Block, FullClient>,
+ (BoxBlockImport, Option<Telemetry>),
+>;
+
+/// Returns most parts of a service. Not enough to run a full chain,
+/// But enough to perform chain operations like purge-chain
+#[allow(clippy::type_complexity)]
+pub fn new_partial(
+ config: &Configuration,
+ fork_config: ForkingConfig,
+) -> Result<Service, ServiceError> {
+ let telemetry = config
+ .telemetry_endpoints
+ .clone()
+ .filter(|x| !x.is_empty())
+ .map(|endpoints| -> Result<_, sc_telemetry::Error> {
+ let worker = TelemetryWorker::new(16)?;
+ let telemetry = worker.handle().new_telemetry(endpoints);
+ Ok((worker, telemetry))
+ })
+ .transpose()?;
+
+ let executor = sc_service::new_wasm_executor::<sp_io::SubstrateHostFunctions>(config);
+ let (client, backend, keystore_container, task_manager) =
+ sc_service::new_full_parts::<Block, RuntimeApi, _>(
+ config,
+ telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
+ executor,
+ )?;
+ let client = Arc::new(client);
+
+ let telemetry = telemetry.map(|(worker, telemetry)| {
+ task_manager
+ .spawn_handle()
+ .spawn("telemetry", None, worker.run());
+ telemetry
+ });
+
+ let select_chain = LongestChain::new(backend.clone());
+
+ let transaction_pool = sc_transaction_pool::BasicPool::new_full(
+ config.transaction_pool.clone(),
+ config.role.is_authority().into(),
+ config.prometheus_registry(),
+ task_manager.spawn_essential_handle(),
+ client.clone(),
+ );
+
+ let block_import = sc_consensus_pow::PowBlockImport::new(
+ client.clone(),
+ client.clone(),
+ MultiPow::new(client.clone(), fork_config),
+ 0, // check inherents starting at block 0
+ select_chain.clone(),
+ move |_, ()| async move {
+ let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
+
+ // We don't need the current mining key to check inherents, so we just use a default.
+ // TODO, I don't think we need to do any checking here at all, right?
+ // So can I just remove the author entirely?
+ let author =
+ academy_pow_runtime::block_author::InherentDataProvider(Default::default());
+
+ Ok((timestamp, author))
+ },
+ );
+
+ let import_queue = sc_consensus_pow::import_queue(
+ Box::new(block_import.clone()),
+ None,
+ MultiPow::new(client.clone(), fork_config),
+ &task_manager.spawn_essential_handle(),
+ config.prometheus_registry(),
+ )?;
+
+ Ok(PartialComponents {
+ client,
+ backend,
+ task_manager,
+ import_queue,
+ keystore_container,
+ select_chain,
+ transaction_pool,
+ other: (Box::new(block_import), telemetry),
+ })
+}
+
+/// Builds a new service for a full client.
+pub fn new_full<
+ N: sc_network::NetworkBackend<Block, <Block as sp_runtime::traits::Block>::Hash>,
+>(
+ config: Configuration,
+ fork_config: ForkingConfig,
+ sr25519_public_key: sr25519::Public,
+ instant_seal: bool,
+ mining_algo: SupportedHashes,
+) -> Result<TaskManager, ServiceError> {
+ let sc_service::PartialComponents {
+ client,
+ backend,
+ mut task_manager,
+ import_queue,
+ keystore_container,
+ select_chain,
+ transaction_pool,
+ other: (pow_block_import, mut telemetry),
+ } = new_partial(&config, fork_config)?;
+
+ let net_config = sc_network::config::FullNetworkConfiguration::<
+ Block,
+ <Block as sp_runtime::traits::Block>::Hash,
+ N,
+ >::new(&config.network);
+ let metrics = sc_network::NotificationMetrics::new(None);
+
+ let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) =
+ sc_service::build_network(sc_service::BuildNetworkParams {
+ config: &config,
+ net_config,
+ client: client.clone(),
+ transaction_pool: transaction_pool.clone(),
+ spawn_handle: task_manager.spawn_handle(),
+ import_queue,
+ block_announce_validator_builder: None,
+ warp_sync_params: None,
+ block_relay: None,
+ metrics: metrics,
+ })?;
+
+ let role = config.role.clone();
+ let prometheus_registry = config.prometheus_registry().cloned();
+
+ let rpc_extensions_builder = {
+ let client = client.clone();
+ let pool = transaction_pool.clone();
+
+ Box::new(move |deny_unsafe, _| {
+ let deps = crate::rpc::FullDeps {
+ client: client.clone(),
+ pool: pool.clone(),
+ deny_unsafe,
+ };
+ crate::rpc::create_full(deps).map_err(Into::into)
+ })
+ };
+
+ sc_service::spawn_tasks(sc_service::SpawnTasksParams {
+ network,
+ client: client.clone(),
+ keystore: keystore_container.keystore(),
+ task_manager: &mut task_manager,
+ transaction_pool: transaction_pool.clone(),
+ rpc_builder: rpc_extensions_builder,
+ backend,
+ system_rpc_tx,
+ tx_handler_controller,
+ sync_service: sync_service.clone(),
+ config,
+ telemetry: telemetry.as_mut(),
+ })?;
+
+ if role.is_authority() {
+ let proposer = sc_basic_authorship::ProposerFactory::new(
+ task_manager.spawn_handle(),
+ client.clone(),
+ transaction_pool.clone(),
+ prometheus_registry.as_ref(),
+ telemetry.as_ref().map(|x| x.handle()),
+ );
+
+ // If instant seal is requested, we just start it. Otherwise, we do the full PoW setup.
+ if instant_seal {
+ let params = sc_consensus_manual_seal::InstantSealParams {
+ block_import: client.clone(),
+ env: proposer,
+ client,
+ pool: transaction_pool,
+ select_chain,
+ consensus_data_provider: None,
+ create_inherent_data_providers: move |_, ()| async move {
+ Ok(sp_timestamp::InherentDataProvider::from_system_time())
+ },
+ };
+
+ let authorship_future = sc_consensus_manual_seal::run_instant_seal(params);
+
+ task_manager.spawn_essential_handle().spawn_blocking(
+ "instant-seal",
+ None,
+ authorship_future,
+ );
+ } else {
+ let (mining_worker, mining_worker_task) = sc_consensus_pow::start_mining_worker(
+ Box::new(pow_block_import),
+ client.clone(),
+ select_chain,
+ MultiPow::new(client, fork_config),
+ proposer,
+ sync_service.clone(),
+ sync_service,
+ // Note the mining algorithm in the pre-runtime digest.
+ // This allows us to know which algo it was in the runtime.
+ // TODO This also makes it possible to remove the algo info from
+ // the seal.
+ Some(PreDigest::from((sr25519_public_key.into(), mining_algo)).encode()),
+ // This code is copied from above. Would be better to not repeat it.
+ move |_, ()| async move {
+ let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
+ // set default `author` following miner specified in CLI
+ let author = academy_pow_runtime::block_author::InherentDataProvider(
+ sr25519_public_key.encode(),
+ );
+
+ Ok((timestamp, author))
+ },
+ std::time::Duration::from_secs(10),
+ std::time::Duration::from_secs(5),
+ );
+
+ task_manager.spawn_essential_handle().spawn_blocking(
+ "pow-miner",
+ Some("pow-mining"),
+ mining_worker_task,
+ );
+
+ // Start Mining worker.
+ //TODO Some of this should move into the multi_pow crate.
+ use multi_pow::{multi_hash_meets_difficulty, Compute};
+ use sp_core::U256;
+ let mut nonce: U256 = U256::from(0);
+ std::thread::spawn(move || loop {
+ let worker = mining_worker.clone();
+ let metadata = worker.metadata();
+ if let Some(metadata) = metadata {
+ let compute = Compute {
+ difficulty: metadata.difficulty,
+ pre_hash: metadata.pre_hash,
+ nonce,
+ };
+ let seal = compute.compute(mining_algo);
+ if multi_hash_meets_difficulty(&seal.work, seal.difficulty) {
+ nonce = U256::from(0);
+ let _ = futures::executor::block_on(worker.submit(seal.encode()));
+ } else {
+ nonce = nonce.saturating_add(U256::from(1));
+ if nonce == U256::MAX {
+ nonce = U256::from(0);
+ }
+ }
+ } else {
+ std::thread::sleep(std::time::Duration::from_secs(1));
+ }
+ });
+ }
+ }
+
+ network_starter.start_network();
+ Ok(task_manager)
+}
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
new file mode 100644
index 0000000..05071e7
--- /dev/null
+++ b/runtime/Cargo.toml
@@ -0,0 +1,77 @@
+[package]
+authors = [ "Parity Technologies <admin@parity.io>", "Joshy Orndorff" ]
+edition = "2021"
+homepage = "https://substrate.dev"
+name = "academy-pow-runtime"
+version = "3.0.0"
+
+[dependencies]
+async-trait = { optional = true, workspace = true }
+parity-scale-codec = { workspace = true }
+scale-info = { workspace = true }
+serde = { optional = true, workspace = true }
+log.workspace = true
+
+frame-executive = { workspace = true }
+frame-support = { workspace = true }
+frame-system = { workspace = true }
+pallet-balances = { workspace = true }
+pallet-timestamp = { workspace = true }
+pallet-transaction-payment = { workspace = true }
+sp-api = { workspace = true }
+sp-block-builder = { workspace = true }
+sp-consensus-pow = { workspace = true }
+sp-core = { workspace = true }
+sp-genesis-builder = { workspace = true }
+sp-inherents = { workspace = true }
+sp-io = { workspace = true }
+sp-keystore = { optional = true, workspace = true }
+sp-offchain = { workspace = true }
+sp-runtime = { workspace = true }
+sp-session = { workspace = true }
+sp-std = { workspace = true }
+sp-transaction-pool = { workspace = true }
+sp-version = { workspace = true }
+
+multi-pow = { default-features = false, path = "../multi-pow" }
+
+# Used for the node's RPCs
+frame-system-rpc-runtime-api = { workspace = true }
+pallet-transaction-payment-rpc-runtime-api = { workspace = true }
+[build-dependencies]
+substrate-wasm-builder = { workspace = true }
+
+
+[dev-dependencies]
+hex-literal = { workspace = true }
+
+[features]
+default = [ "std" ]
+std = [
+ "pallet-balances/std",
+ "parity-scale-codec/std",
+ "frame-executive/std",
+ "frame-support/std",
+ "serde",
+ "sp-api/std",
+ "sp-block-builder/std",
+ "sp-consensus-pow/std",
+ "sp-core/std",
+ "sp-genesis-builder/std",
+ "sp-inherents/std",
+ "sp-io/std",
+ "sp-offchain/std",
+ "sp-runtime/std",
+ "sp-session/std",
+ "sp-std/std",
+ "sp-transaction-pool/std",
+ "sp-version/std",
+ "frame-system/std",
+ "pallet-timestamp/std",
+ "async-trait",
+ "sp-keystore",
+ "pallet-transaction-payment/std",
+ "pallet-transaction-payment-rpc-runtime-api/std",
+ "frame-system-rpc-runtime-api/std",
+ "multi-pow/std",
+]
diff --git a/runtime/build.rs b/runtime/build.rs
new file mode 100644
index 0000000..6db3d90
--- /dev/null
+++ b/runtime/build.rs
@@ -0,0 +1,8 @@
+fn main() {
+ #[cfg(feature = "std")]
+ substrate_wasm_builder::WasmBuilder::new()
+ .with_current_project()
+ .export_heap_base()
+ .import_memory()
+ .build()
+}
diff --git a/runtime/src/block_author.rs b/runtime/src/block_author.rs
new file mode 100644
index 0000000..b147eeb
--- /dev/null
+++ b/runtime/src/block_author.rs
@@ -0,0 +1,187 @@
+//! This pallet allows block authors to self-identify by providing an sr25519 public key
+//!
+//! The included trait allows other pallets to fetch the author's account as long as the
+//! runtime's AccountId type can be created from an sr25519 public key.
+
+pub use pallet::*;
+use parity_scale_codec::{Decode, Encode};
+use sp_core::sr25519;
+#[cfg(feature = "std")]
+use sp_inherents::InherentData;
+use sp_inherents::{InherentIdentifier, IsFatalError};
+use sp_runtime::RuntimeString;
+use sp_std::vec::Vec;
+
+#[frame_support::pallet(dev_mode)]
+pub mod pallet {
+ use frame_support::pallet_prelude::*;
+ use frame_system::pallet_prelude::*;
+
+ use super::*;
+
+ /// The BlockAuthor Inherent pallet.
+ #[pallet::pallet]
+ pub struct Pallet<T>(PhantomData<T>);
+ /// The pallet's configuration trait. Nothing to configure.
+ #[pallet::config]
+ pub trait Config: frame_system::Config {
+ fn on_author_set(_author_account: Self::AccountId) {}
+ }
+
+ #[pallet::error]
+ pub enum Error<T> {
+ /// Author already set in block.
+ AuthorAlreadySet,
+ }
+
+ /// Author of current block.
+ #[pallet::storage]
+ pub type Author<T: Config> = StorageValue<_, sr25519::Public, OptionQuery>;
+
+ #[pallet::call]
+ impl<T: Config> Pallet<T>
+ where
+ <T as frame_system::Config>::AccountId: From<sp_core::sr25519::Public>,
+ {
+ /// Inherent to set the author of a block
+ #[pallet::weight(1_000_000)]
+ pub fn set_author(origin: OriginFor<T>, author: sr25519::Public) -> DispatchResult {
+ ensure_none(origin)?;
+ ensure!(Author::<T>::get().is_none(), Error::<T>::AuthorAlreadySet);
+
+ // Store the author in case other pallets want to fetch it and to let
+ // offchain tools inspect it
+ Author::<T>::put(author);
+
+ // Call the hook
+ T::on_author_set(author.into());
+
+ Ok(())
+ }
+ }
+
+ #[pallet::hooks]
+ impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
+ fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
+ // Reset the author to None at the beginning of the block
+ Author::<T>::kill();
+
+ // Return zero weight because we are not using weight-based
+ // transaction fees.
+ Weight::zero()
+ }
+ }
+
+ #[pallet::inherent]
+ impl<T: Config> ProvideInherent for Pallet<T>
+ where
+ <T as frame_system::Config>::AccountId: From<sp_core::sr25519::Public>,
+ {
+ type Call = Call<T>;
+ type Error = InherentError;
+ const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
+
+ fn is_inherent_required(_: &InherentData) -> Result<Option<Self::Error>, Self::Error> {
+ // Return Ok(Some(_)) unconditionally because this inherent is required in every block
+ // If it is not found, throw an AuthorInherentRequired error.
+ Ok(Some(InherentError::Other(
+ sp_runtime::RuntimeString::Borrowed("BlockAuthorInherentRequired"),
+ )))
+ }
+
+ fn create_inherent(data: &InherentData) -> Option<Self::Call> {
+ // Grab the Vec<u8> labelled with "author_" from the map of all inherent data
+ let author_raw = data
+ .get_data::<InherentType>(&INHERENT_IDENTIFIER)
+ .expect("Gets and decodes authorship inherent data")?;
+
+ // Decode the Vec<u8> into an actual author
+ let author = sr25519::Public::decode(&mut &author_raw[..])
+ .expect("Decodes author raw inherent data");
+
+ Some(Call::set_author { author })
+ }
+
+ fn is_inherent(call: &Self::Call) -> bool {
+ matches!(call, Call::set_author { .. })
+ }
+ }
+}
+
+/// A trait to find the author (miner) of the block.
+pub trait BlockAuthor {
+ fn block_author() -> Option<sr25519::Public>;
+}
+
+impl BlockAuthor for () {
+ fn block_author() -> Option<sr25519::Public> {
+ None
+ }
+}
+
+impl<T: Config> BlockAuthor for Pallet<T> {
+ fn block_author() -> Option<sr25519::Public> {
+ Author::<T>::get()
+ }
+}
+
+pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"author__";
+
+#[derive(Encode, Decode, Debug)]
+pub enum InherentError {
+ Other(RuntimeString),
+}
+
+impl IsFatalError for InherentError {
+ fn is_fatal_error(&self) -> bool {
+ match *self {
+ InherentError::Other(_) => true,
+ }
+ }
+}
+
+impl InherentError {
+ /// Try to create an instance ouf of the given identifier and data.
+ #[cfg(feature = "std")]
+ pub fn try_from(id: &InherentIdentifier, data: &[u8]) -> Option<Self> {
+ if id == &INHERENT_IDENTIFIER {
+ <InherentError as parity_scale_codec::Decode>::decode(&mut &data[..]).ok()
+ } else {
+ None
+ }
+ }
+}
+
+/// The type of data that the inherent will contain.
+/// Just a byte array. It will be decoded to an actual pubkey later
+pub type InherentType = Vec<u8>;
+
+#[cfg(feature = "std")]
+pub struct InherentDataProvider(pub InherentType);
+
+#[cfg(feature = "std")]
+#[async_trait::async_trait]
+impl sp_inherents::InherentDataProvider for InherentDataProvider {
+ async fn provide_inherent_data(
+ &self,
+ inherent_data: &mut InherentData,
+ ) -> Result<(), sp_inherents::Error> {
+ inherent_data.put_data(INHERENT_IDENTIFIER, &self.0)
+ }
+
+ async fn try_handle_error(
+ &self,
+ identifier: &InherentIdentifier,
+ _error: &[u8],
+ ) -> Option<Result<(), sp_inherents::Error>> {
+ // Dont' process modules from other inherents
+ if *identifier != INHERENT_IDENTIFIER {
+ return None;
+ }
+
+ // All errors with the author inehrent are fatal
+ Some(Err(sp_inherents::Error::Application(Box::from(
+ String::from("Error processing author inherent"),
+ ))))
+ }
+}
diff --git a/runtime/src/difficulty.rs b/runtime/src/difficulty.rs
new file mode 100644
index 0000000..babfeab
--- /dev/null
+++ b/runtime/src/difficulty.rs
@@ -0,0 +1,203 @@
+//! A difficulty adjustment algorithm (DAA) to keep the block time close to a particular goal
+//! Cribbed from Kulupu https://github.com/kulupu/kulupu/blob/master/runtime/src/difficulty.rs
+//!
+//! It is possible to implement other DAAs such as that of BTC and BCH. This would be an interesting
+//! and worth-while experiment. The DAAs should be abstracted away with a trait.
+//! Some ideas: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3410460
+
+use core::cmp::{max, min};
+
+use frame_support::traits::Time;
+use parity_scale_codec::{Decode, Encode, MaxEncodedLen};
+use scale_info::TypeInfo;
+use sp_core::U256;
+use sp_runtime::traits::UniqueSaturatedInto;
+
+#[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, Debug, MaxEncodedLen, TypeInfo)]
+pub struct DifficultyAndTimestamp<M> {
+ pub difficulty: Difficulty,
+ pub timestamp: M,
+}
+
+/// Move value linearly toward a goal
+pub fn damp(actual: u128, goal: u128, damp_factor: u128) -> u128 {
+ (actual + (damp_factor - 1) * goal) / damp_factor
+}
+
+/// Limit value to be within some factor from a goal
+pub fn clamp(actual: u128, goal: u128, clamp_factor: u128) -> u128 {
+ max(goal / clamp_factor, min(actual, goal * clamp_factor))
+}
+
+const DIFFICULTY_ADJUST_WINDOW: u128 = 60;
+type Difficulty = U256;
+
+pub use pallet::*;
+
+#[frame_support::pallet(dev_mode)]
+pub mod pallet {
+ use frame_support::pallet_prelude::*;
+ use frame_system::pallet_prelude::*;
+
+ use super::*;
+
+ /// Pallet's configuration trait.
+ #[pallet::config]
+ pub trait Config<I: 'static = ()>: frame_system::Config {
+ /// A Source for timestamp data
+ type TimeProvider: Time;
+ /// The block time that the DAA will attempt to maintain
+ type TargetBlockTime: Get<u128>;
+ /// Dampening factor to use for difficulty adjustment
+ type DampFactor: Get<u128>;
+ /// Clamp factor to use for difficulty adjustment
+ /// Limit value to within this factor of goal. Recommended value: 2
+ type ClampFactor: Get<u128>;
+ /// The maximum difficulty allowed. Recommended to use u128::max_value()
+ type MaxDifficulty: Get<u128>;
+ /// Minimum difficulty, enforced in difficulty retargetting
+ /// avoids getting stuck when trying to increase difficulty subject to dampening
+ /// Recommended to use same value as DampFactor
+ type MinDifficulty: Get<u128>;
+
+ /// Now that the pallet is instantiable, we need a way to decide which blocks are
+ /// relevant to this instance. This function does just that.
+ ///
+ /// The default implementation assumes that all blocks are relevant which is what
+ /// you probably want when there is only a single instance.
+ fn relevant_to_this_instance() -> bool {
+ true
+ }
+ }
+
+ #[pallet::pallet]
+ pub struct Pallet<T, I = ()>(_);
+
+ type DifficultyList<T, I> =
+ [Option<DifficultyAndTimestamp<<<T as Config<I>>::TimeProvider as Time>::Moment>>; 60];
+
+ /// Past difficulties and timestamps, from earliest to latest.
+ #[pallet::storage]
+ pub type PastDifficultiesAndTimestamps<T: Config<I>, I: 'static = ()> =
+ StorageValue<_, DifficultyList<T, I>, ValueQuery, EmptyList<T, I>>;
+
+ pub struct EmptyList<T: Config<I>, I: 'static = ()>(PhantomData<(T, I)>);
+ impl<T: Config<I>, I> Get<DifficultyList<T, I>> for EmptyList<T, I> {
+ fn get() -> DifficultyList<T, I> {
+ [None; DIFFICULTY_ADJUST_WINDOW as usize]
+ }
+ }
+
+ /// Current difficulty.
+ #[pallet::storage]
+ #[pallet::getter(fn difficulty)]
+ pub type CurrentDifficulty<T: Config<I>, I: 'static = ()> =
+ StorageValue<_, Difficulty, ValueQuery>;
+
+ /// Initial difficulty.
+ #[pallet::storage]
+ pub type InitialDifficulty<T: Config<I>, I: 'static = ()> =
+ StorageValue<_, Difficulty, ValueQuery>;
+
+ #[pallet::genesis_config]
+ pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
+ pub _ph_data: Option<PhantomData<(T, I)>>,
+ pub initial_difficulty: [u8; 32], // Difficulty = U256
+ }
+
+ #[pallet::genesis_build]
+ impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
+ fn build(&self) {
+ let initial_difficulty = U256::from_little_endian(&self.initial_difficulty);
+ // Initialize the Current difficulty
+ CurrentDifficulty::<T, I>::put(&initial_difficulty);
+
+ // Store the initial difficulty in storage because we will need it
+ // during the first DIFFICULTY_ADJUSTMENT_WINDOW blocks (see todo below).
+ InitialDifficulty::<T, I>::put(&initial_difficulty);
+ }
+ }
+
+ impl<T: Config<I>, I: 'static> Default for GenesisConfig<T, I> {
+ fn default() -> Self {
+ GenesisConfig {
+ _ph_data: Default::default(),
+ initial_difficulty: [0u8; 32],
+ }
+ }
+ }
+
+ #[pallet::hooks]
+ impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
+ fn on_finalize(_n: BlockNumberFor<T>) {
+ // First check if this is block is relevant to this instance of the difficulty adjustment algorithm
+ if !T::relevant_to_this_instance() {
+ return;
+ }
+
+ let mut data = PastDifficultiesAndTimestamps::<T, I>::get();
+
+ for i in 1..data.len() {
+ data[i - 1] = data[i];
+ }
+
+ data[data.len() - 1] = Some(DifficultyAndTimestamp {
+ timestamp: T::TimeProvider::now(),
+ difficulty: Self::difficulty(),
+ });
+
+ let mut ts_delta = 0;
+ for i in 1..(DIFFICULTY_ADJUST_WINDOW as usize) {
+ let prev: Option<u128> = data[i - 1].map(|d| d.timestamp.unique_saturated_into());
+ let cur: Option<u128> = data[i].map(|d| d.timestamp.unique_saturated_into());
+
+ let delta = match (prev, cur) {
+ (Some(prev), Some(cur)) => cur.saturating_sub(prev),
+ _ => T::TargetBlockTime::get(),
+ };
+ ts_delta += delta;
+ }
+
+ if ts_delta == 0 {
+ ts_delta = 1;
+ }
+
+ let mut diff_sum = U256::zero();
+ //TODO Could we just initialize every array cell to the initial difficulty to not need the
+ // separate storage item?
+ for item in data.iter().take(DIFFICULTY_ADJUST_WINDOW as usize) {
+ let diff = match item.map(|d| d.difficulty) {
+ Some(diff) => diff,
+ None => InitialDifficulty::<T, I>::get(),
+ };
+ diff_sum += diff;
+ }
+
+ if diff_sum < U256::from(T::MinDifficulty::get()) {
+ diff_sum = U256::from(T::MinDifficulty::get());
+ }
+
+ // Calculate the average length of the adjustment window
+ let adjustment_window = DIFFICULTY_ADJUST_WINDOW * T::TargetBlockTime::get();
+
+ // adjust time delta toward goal subject to dampening and clamping
+ let adj_ts = clamp(
+ damp(ts_delta, adjustment_window, T::DampFactor::get()),
+ adjustment_window,
+ T::ClampFactor::get(),
+ );
+
+ // minimum difficulty avoids getting stuck due to dampening
+ let difficulty = min(
+ U256::from(T::MaxDifficulty::get()),
+ max(
+ U256::from(T::MinDifficulty::get()),
+ diff_sum * U256::from(T::TargetBlockTime::get()) / U256::from(adj_ts),
+ ),
+ );
+
+ <PastDifficultiesAndTimestamps<T, I>>::put(data);
+ <CurrentDifficulty<T, I>>::put(difficulty);
+ }
+ }
+}
diff --git a/runtime/src/issuance.rs b/runtime/src/issuance.rs
new file mode 100644
index 0000000..9a11f9d
--- /dev/null
+++ b/runtime/src/issuance.rs
@@ -0,0 +1,47 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+/// A trait for types that can provide the amount of issuance to award to the block
+/// author for the given block number.
+pub trait Issuance<BlockNumber, Balance> {
+ fn issuance(block: BlockNumber) -> Balance;
+}
+
+// Minimal implementations for when you don't actually want any issuance
+impl Issuance<u32, u128> for () {
+ fn issuance(_block: u32) -> u128 {
+ 0
+ }
+}
+
+impl Issuance<u64, u128> for () {
+ fn issuance(_block: u64) -> u128 {
+ 0
+ }
+}
+
+/// A type that provides block issuance according to bitcoin's rules
+/// Initial issuance is 50 / block
+/// Issuance is cut in half every 210,000 blocks
+/// cribbed from github.com/Bitcoin-ABC/bitcoin-abc/blob/9c7b12e6f128a59423f4de3d6d4b5231ebe9aac2/src/validation.cpp#L1007
+
+pub struct BitcoinHalving;
+
+/// The number of blocks between each halving.
+const HALVING_INTERVAL: u32 = 210_000;
+/// The per-block issuance before any halving. Decimal places should be accounted for here.
+const INITIAL_ISSUANCE: u32 = 50;
+
+impl Issuance<u32, u128> for BitcoinHalving {
+ fn issuance(block: u32) -> u128 {
+ let halving = block / HALVING_INTERVAL;
+ // Force block reward to zero when right shift is undefined.
+ if halving >= 64 {
+ return 0;
+ }
+
+ // Approximately, 600 seconds (or 10 minutes) for a block to be finalized.
+ // Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years.
+ // Divided by 2 using bitwise
+ (INITIAL_ISSUANCE >> halving).into()
+ }
+}
diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
new file mode 100644
index 0000000..5b09407
--- /dev/null
+++ b/runtime/src/lib.rs
@@ -0,0 +1,529 @@
+//! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm.
+
+#![cfg_attr(not(feature = "std"), no_std)]
+// The construct runtime macro does a lot of recursion and requires us to increase the limit to 256.
+#![recursion_limit = "256"]
+
+// Make the WASM binary available.
+#[cfg(feature = "std")]
+include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
+
+pub use frame_support::{
+ construct_runtime, parameter_types, derive_impl,
+ traits::{
+ Currency, EstimateNextNewSession, Imbalance, IsSubType, KeyOwnerProofSystem,
+ LockIdentifier, Nothing, OnUnbalanced, ValidatorSet, VariantCountOf,
+ },
+ weights::{
+ constants::{
+ BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND,
+ },
+ IdentityFee, Weight,
+ },
+ Callable, StorageValue,
+};
+use frame_support::{
+ genesis_builder_helper::{build_state, get_preset},
+ instances::{Instance1, Instance2, Instance3},
+ sp_runtime::Perquintill,
+ traits::{ConstU128, ConstU32, ConstU8},
+};
+use multi_pow::SupportedHashes;
+pub use pallet_balances::Call as BalancesCall;
+pub use pallet_timestamp::Call as TimestampCall;
+use pallet_transaction_payment::{ConstFeeMultiplier, FungibleAdapter, Multiplier};
+use parity_scale_codec::Decode;
+use sp_api::impl_runtime_apis;
+use sp_consensus_pow::POW_ENGINE_ID;
+use sp_core::OpaqueMetadata;
+// A few exports that help ease life for downstream crates.
+#[cfg(any(feature = "std", test))]
+pub use sp_runtime::BuildStorage;
+use sp_runtime::{
+ create_runtime_str, generic,
+ traits::{
+ AccountIdLookup, BlakeTwo256, Block as BlockT, Bounded, IdentifyAccount, One, Verify,
+ },
+ transaction_validity::{
+ InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError,
+ },
+ ApplyExtrinsicResult, DigestItem, MultiSignature,
+ ExtrinsicInclusionMode,
+};
+pub use sp_runtime::{FixedPointNumber, Perbill, Permill};
+use sp_std::prelude::*;
+#[cfg(feature = "std")]
+use sp_version::NativeVersion;
+use sp_version::RuntimeVersion;
+/// An index to a block.
+pub type BlockNumber = u32;
+
+/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
+pub type Signature = MultiSignature;
+
+/// Some way of identifying an account on the chain. We intentionally make it equivalent
+/// to the public key of our transaction signing scheme.
+pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
+
+/// The type for looking up accounts. We don't expect more than 4 billion of them, but you
+/// never know...
+pub type AccountIndex = u32;
+
+/// Balance of an account.
+pub type Balance = u128;
+
+/// Index of a transaction in the chain.
+pub type Nonce = u32;
+
+/// Index of a transaction in the chain.
+pub type Index = u32;
+
+/// A hash of some data used by the chain.
+pub type Hash = sp_core::H256;
+
+/// Consensus digest containing block author and supported hash algorithm.
+pub type PreDigest = (AccountId, SupportedHashes);
+
+/// The BlockAuthor trait in `./block_author.rs`
+pub mod block_author;
+
+// /// The Difficulty Adjustment Algorithm in `./difficulty.rs`
+pub mod difficulty;
+
+/// The total issuance and halving time
+pub mod issuance;
+
+/// UTXOs serve as the digital equivalent of change you receive after making a cash purchase
+pub mod utxo;
+
+/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know
+/// the specifics of the runtime. They can then be made to be agnostic over specific formats
+/// of data like extrinsics, allowing for them to continue syncing the network through upgrades
+/// to even the core data structures.
+pub mod opaque {
+ pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
+
+ use super::*;
+
+ /// Opaque block header type.
+ pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
+ /// Opaque block type.
+ pub type Block = generic::Block<Header, UncheckedExtrinsic>;
+ /// Opaque block identifier type.
+ pub type BlockId = generic::BlockId<Block>;
+}
+
+/// This runtime version.
+#[sp_version::runtime_version]
+pub const VERSION: RuntimeVersion = RuntimeVersion {
+ spec_name: create_runtime_str!("academy-pow"),
+ impl_name: create_runtime_str!("academy-pow"),
+ authoring_version: 1,
+ spec_version: 1,
+ impl_version: 1,
+ apis: RUNTIME_API_VERSIONS,
+ transaction_version: 1,
+ state_version: 1,
+};
+
+/// The version information used to identify this runtime when compiled natively.
+#[cfg(feature = "std")]
+pub fn native_version() -> NativeVersion {
+ NativeVersion {
+ runtime_version: VERSION,
+ can_author_with: Default::default(),
+ }
+}
+
+const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
+// native chain currency
+pub const TOKEN_SYMBOL: &str = "Unit";
+pub const TOKEN_DECIMALS: u32 = 12;
+pub const TOKEN: u128 = 10u128.pow(TOKEN_DECIMALS);
+
+parameter_types! {
+ pub const BlockHashCount: BlockNumber = 2400;
+ pub const Version: RuntimeVersion = VERSION;
+ /// We allow for 2 seconds of compute with a 6 second average block time.
+ pub BlockWeights: frame_system::limits::BlockWeights =
+ frame_system::limits::BlockWeights::with_sensible_defaults(
+ Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX),
+ NORMAL_DISPATCH_RATIO,
+ );
+ pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength
+ ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);
+ pub const SS58Prefix: u8 = 42;
+}
+
+#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)]
+impl frame_system::Config for Runtime {
+ /// The basic call filter to use in dispatchable.
+ type BaseCallFilter = frame_support::traits::Everything;
+ /// Block & extrinsics weights: base values and limits.
+ type BlockWeights = BlockWeights;
+ /// The maximum length of a block (in bytes).
+ type BlockLength = BlockLength;
+ /// The identifier used to distinguish between accounts.
+ type AccountId = AccountId;
+ /// The aggregated dispatch type that is available for extrinsics.
+ type RuntimeCall = RuntimeCall;
+ /// The lookup mechanism to get account ID from whatever is passed in dispatchers.
+ type Lookup = AccountIdLookup<AccountId, ()>;
+ /// The type for hashing blocks and tries.
+ type Hash = Hash;
+ /// The hashing algorithm used.
+ type Hashing = BlakeTwo256;
+ /// The ubiquitous event type.
+ type RuntimeEvent = RuntimeEvent;
+ /// The ubiquitous origin type.
+ type RuntimeOrigin = RuntimeOrigin;
+ /// Maximum number of block number to block hash mappings to keep (oldest pruned first).
+ type BlockHashCount = BlockHashCount;
+ /// The weight of database operations that the runtime can invoke.
+ type DbWeight = RocksDbWeight;
+ /// Version of the runtime.
+ type Version = Version;
+ /// This type is being generated by the construct runtime macro.
+ type PalletInfo = PalletInfo;
+ /// What to do if a new account is created.
+ type OnNewAccount = ();
+ /// What to do if an account is fully reaped from the system.
+ type OnKilledAccount = ();
+ /// The data to be stored in an account.
+ type AccountData = pallet_balances::AccountData<Balance>;
+ /// Weight information for the extrinsics of this pallet.
+ type SystemWeightInfo = ();
+ /// This is used as an identifier of the chain. 42 is the generic substrate prefix.
+ type SS58Prefix = SS58Prefix;
+ /// The set code logic, just the default since we're not a parachain.
+ type OnSetCode = ();
+ type MaxConsumers = frame_support::traits::ConstU32<16>;
+ type Nonce = Nonce;
+ type Block = Block;
+}
+
+parameter_types! {
+ pub const MinimumPeriod: u64 = 1000;
+}
+
+impl pallet_timestamp::Config for Runtime {
+ /// A timestamp: milliseconds since the unix epoch.
+ type Moment = u64;
+ type OnTimestampSet = ();
+ type MinimumPeriod = MinimumPeriod;
+ type WeightInfo = ();
+}
+
+impl pallet_balances::Config for Runtime {
+ type MaxLocks = ConstU32<50>;
+ type MaxReserves = ();
+ type ReserveIdentifier = [u8; 8];
+ /// The type for recording an account's balance.
+ type Balance = Balance;
+ /// The ubiquitous event type.
+ type RuntimeEvent = RuntimeEvent;
+ type DustRemoval = ();
+ type ExistentialDeposit = ConstU128<500>;
+ type AccountStore = System;
+ type WeightInfo = pallet_balances::weights::SubstrateWeight<Runtime>;
+ type FreezeIdentifier = RuntimeFreezeReason;
+ type MaxFreezes = VariantCountOf<RuntimeFreezeReason>;
+ type RuntimeHoldReason = RuntimeHoldReason;
+ type RuntimeFreezeReason = RuntimeFreezeReason;
+}
+
+parameter_types! {
+ pub const TargetBlockTime: u128 = 5_000;
+ // Setting min difficulty to damp factor per recommendation
+ pub const DampFactor: u128 = 3;
+ pub const ClampFactor: u128 = 2;
+ pub const MaxDifficulty: u128 = u128::max_value();
+}
+
+// Helper function to get the current blocks PoW algo from the predigest
+fn current_blocks_mining_algo() -> SupportedHashes {
+ System::digest()
+ .logs
+ .iter()
+ .find_map(|digest_item| match digest_item {
+ DigestItem::PreRuntime(POW_ENGINE_ID, pre_digest) => {
+ PreDigest::decode(&mut &pre_digest[..]).map(|d| d.1).ok()
+ }
+ _ => None,
+ })
+ .expect("There should be exactly one pow pre-digest item")
+}
+
+impl difficulty::Config<Instance1> for Runtime {
+ type TimeProvider = Timestamp;
+ type TargetBlockTime = TargetBlockTime;
+ type DampFactor = DampFactor;
+ type ClampFactor = ClampFactor;
+ type MaxDifficulty = MaxDifficulty;
+ type MinDifficulty = DampFactor;
+
+ fn relevant_to_this_instance() -> bool {
+ current_blocks_mining_algo() == SupportedHashes::Md5
+ }
+}
+
+impl difficulty::Config<Instance2> for Runtime {
+ type TimeProvider = Timestamp;
+ type TargetBlockTime = TargetBlockTime;
+ type DampFactor = DampFactor;
+ type ClampFactor = ClampFactor;
+ type MaxDifficulty = MaxDifficulty;
+ type MinDifficulty = DampFactor;
+
+ fn relevant_to_this_instance() -> bool {
+ current_blocks_mining_algo() == SupportedHashes::Sha3
+ }
+}
+
+impl difficulty::Config<Instance3> for Runtime {
+ type TimeProvider = Timestamp;
+ type TargetBlockTime = TargetBlockTime;
+ type DampFactor = DampFactor;
+ type ClampFactor = ClampFactor;
+ type MaxDifficulty = MaxDifficulty;
+ type MinDifficulty = DampFactor;
+
+ fn relevant_to_this_instance() -> bool {
+ current_blocks_mining_algo() == SupportedHashes::Keccak
+ }
+}
+
+impl block_author::Config for Runtime {
+ // Each block mined issues 50 new tokens to the miner
+ fn on_author_set(author_account: Self::AccountId) {
+ let issuance = 50 * TOKEN;
+ let _ = Balances::deposit_creating(&author_account, issuance);
+ }
+}
+
+parameter_types! {
+ // This value increases the priority of `Operational` transactions by adding
+ // a "virtual tip" that's equal to the `OperationalFeeMultiplier * final_fee`.
+ // follows polkadot : https://github.com/paritytech/polkadot/blob/9ce5f7ef5abb1a4291454e8c9911b304d80679f9/runtime/polkadot/src/lib.rs#L369
+ pub const OperationalFeeMultiplier: u8 = 5;
+ // We expect that on average 25% of the normal capacity will be occupied with normal txs.
+ pub const TargetSaturationLevel: Perquintill = Perquintill::from_percent(25);
+ // During 20 blocks the fee may not change more than by 100%. This, together with the
+ // `TargetSaturationLevel` value, results in variability ~0.067. For the corresponding
+ // formulas please refer to Substrate code at `frame/transaction-payment/src/lib.rs`.
+ pub FeeVariability: Multiplier = Multiplier::saturating_from_rational(67, 1000);
+ // Fee should never be lower than the computational cost.
+ pub MinimumMultiplier: Multiplier = Multiplier::one();
+ pub MaximumMultiplier: Multiplier = Bounded::max_value();
+}
+
+parameter_types! {
+ pub FeeMultiplier: Multiplier = Multiplier::one();
+}
+
+impl pallet_transaction_payment::Config for Runtime {
+ type RuntimeEvent = RuntimeEvent;
+ type OnChargeTransaction = FungibleAdapter<Balances, ()>;
+ type OperationalFeeMultiplier = ConstU8<5>;
+ type WeightToFee = IdentityFee<Balance>;
+ type LengthToFee = IdentityFee<Balance>;
+ type FeeMultiplierUpdate = ConstFeeMultiplier<FeeMultiplier>;
+}
+
+construct_runtime!(
+ pub struct Runtime {
+ System: frame_system,
+ Timestamp: pallet_timestamp,
+ Balances: pallet_balances,
+ TransactionPayment: pallet_transaction_payment,
+ Md5DifficultyAdjustment: difficulty::<Instance1>,
+ Sha3DifficultyAdjustment: difficulty::<Instance2>,
+ KeccakDifficultyAdjustment: difficulty::<Instance3>,
+ BlockAuthor: block_author,
+ }
+);
+
+/// The address format for describing accounts.
+pub type Address = sp_runtime::MultiAddress<AccountId, ()>;
+/// Block header type as expected by this runtime.
+pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
+/// Block type as expected by this runtime.
+pub type Block = generic::Block<Header, UncheckedExtrinsic>;
+/// A Block signed with a Justification
+pub type SignedBlock = generic::SignedBlock<Block>;
+/// The SignedExtension to the basic transaction logic.
+pub type SignedExtra = (
+ frame_system::CheckNonZeroSender<Runtime>,
+ frame_system::CheckSpecVersion<Runtime>,
+ frame_system::CheckTxVersion<Runtime>,
+ frame_system::CheckGenesis<Runtime>,
+ frame_system::CheckEra<Runtime>,
+ frame_system::CheckNonce<Runtime>,
+ frame_system::CheckWeight<Runtime>,
+ pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
+);
+/// Unchecked extrinsic type as expected by this runtime.
+pub type UncheckedExtrinsic =
+ generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>;
+/// Executive: handles dispatch to the various modules.
+pub type Executive = frame_executive::Executive<
+ Runtime,
+ Block,
+ frame_system::ChainContext<Runtime>,
+ Runtime,
+ AllPalletsWithSystem,
+>;
+
+impl_runtime_apis! {
+ impl sp_api::Core<Block> for Runtime {
+ fn version() -> RuntimeVersion {
+ VERSION
+ }
+
+ fn execute_block(block: Block) {
+ Executive::execute_block(block)
+ }
+
+ fn initialize_block(header: &<Block as BlockT>::Header) -> ExtrinsicInclusionMode {
+ Executive::initialize_block(header)
+ }
+ }
+
+ impl sp_api::Metadata<Block> for Runtime {
+ fn metadata() -> OpaqueMetadata {
+ OpaqueMetadata::new(Runtime::metadata().into())
+ }
+
+ fn metadata_at_version(version: u32) -> Option<OpaqueMetadata> {
+ Runtime::metadata_at_version(version)
+ }
+
+ fn metadata_versions() -> sp_std::vec::Vec<u32> {
+ Runtime::metadata_versions()
+ }
+ }
+
+ impl sp_block_builder::BlockBuilder<Block> for Runtime {
+ fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {
+ Executive::apply_extrinsic(extrinsic)
+ }
+
+ fn finalize_block() -> <Block as BlockT>::Header {
+ Executive::finalize_block()
+ }
+
+ fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {
+ data.create_extrinsics()
+ }
+
+ fn check_inherents(
+ block: Block,
+ data: sp_inherents::InherentData,
+ ) -> sp_inherents::CheckInherentsResult {
+ data.check_extrinsics(&block)
+ }
+ }
+
+ impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {
+ fn validate_transaction(
+ source: TransactionSource,
+ tx: <Block as BlockT>::Extrinsic,
+ block_hash: <Block as BlockT>::Hash,
+ ) -> TransactionValidity {
+ Executive::validate_transaction(source, tx, block_hash)
+ }
+ }
+
+ impl sp_offchain::OffchainWorkerApi<Block> for Runtime {
+ fn offchain_worker(header: &<Block as BlockT>::Header) {
+ Executive::offchain_worker(header)
+ }
+ }
+
+ impl sp_session::SessionKeys<Block> for Runtime {
+ fn generate_session_keys(_seed: Option<Vec<u8>>) -> Vec<u8> {
+ Vec::new()
+ }
+
+ fn decode_session_keys(
+ _encoded: Vec<u8>,
+ ) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
+ None
+ }
+ }
+
+ impl sp_consensus_pow::DifficultyApi<Block, multi_pow::Threshold> for Runtime {
+ fn difficulty() -> multi_pow::Threshold {
+ multi_pow::Threshold {
+ md5: Md5DifficultyAdjustment::difficulty(),
+ sha3: Sha3DifficultyAdjustment::difficulty(),
+ keccak: KeccakDifficultyAdjustment::difficulty(),
+ }
+ }
+ }
+
+ impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {
+ fn account_nonce(account: AccountId) -> Index {
+ System::account_nonce(account)
+ }
+ }
+
+ impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime
+ {
+ fn query_info(
+ uxt: <Block as BlockT>::Extrinsic,
+ len: u32,
+ ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> {
+ TransactionPayment::query_info(uxt, len)
+ }
+ fn query_fee_details(
+ uxt: <Block as BlockT>::Extrinsic,
+ len: u32,
+ ) -> pallet_transaction_payment::FeeDetails<Balance> {
+ TransactionPayment::query_fee_details(uxt, len)
+ }
+ fn query_weight_to_fee(weight: Weight) -> Balance {
+ TransactionPayment::weight_to_fee(weight)
+ }
+ fn query_length_to_fee(length: u32) -> Balance {
+ TransactionPayment::length_to_fee(length)
+ }
+ }
+
+ impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi<Block, Balance, RuntimeCall>
+ for Runtime
+ {
+ fn query_call_info(
+ call: RuntimeCall,
+ len: u32,
+ ) -> pallet_transaction_payment::RuntimeDispatchInfo<Balance> {
+ TransactionPayment::query_call_info(call, len)
+ }
+ fn query_call_fee_details(
+ call: RuntimeCall,
+ len: u32,
+ ) -> pallet_transaction_payment::FeeDetails<Balance> {
+ TransactionPayment::query_call_fee_details(call, len)
+ }
+ fn query_weight_to_fee(weight: Weight) -> Balance {
+ TransactionPayment::weight_to_fee(weight)
+ }
+ fn query_length_to_fee(length: u32) -> Balance {
+ TransactionPayment::length_to_fee(length)
+ }
+ }
+
+ impl sp_genesis_builder::GenesisBuilder<Block> for Runtime {
+ fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result {
+ build_state::<RuntimeGenesisConfig>(config)
+ }
+
+ fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
+ get_preset::<RuntimeGenesisConfig>(id, |_| None)
+ }
+
+ fn preset_names() -> Vec<sp_genesis_builder::PresetId> {
+ Default::default()
+ }
+ }
+}
diff --git a/runtime/src/utxo.rs b/runtime/src/utxo.rs
new file mode 100644
index 0000000..d51c203
--- /dev/null
+++ b/runtime/src/utxo.rs
@@ -0,0 +1,31 @@
+// We make sure this pallet uses `no_std` for compiling to Wasm.
+#![cfg_attr(not(feature = "std"), no_std)]
+
+use parity_scale_codec::{Decode, Encode};
+use scale_info::TypeInfo;
+#[cfg(feature = "std")]
+use serde::{Deserialize, Serialize};
+use sp_core::{
+ sr25519::{Public, Signature},
+ ByteArray, H256, H512,
+};
+use sp_runtime::traits::{BlakeTwo256, Hash, SaturatedConversion};
+use sp_std::{collections::btree_map::BTreeMap, vec::Vec};
+
+use super::{block_author::BlockAuthor, issuance::Issuance};
+
+pub use pallet::*;
+
+#[frame_support::pallet(dev_mode)]
+pub mod pallet {
+ use frame_support::pallet_prelude::*;
+ use frame_system::pallet_prelude::*;
+
+ use super::*;
+
+ #[pallet::config]
+ pub trait Config: frame_system::Config { }
+
+ #[pallet::pallet]
+ pub struct Pallet<T>(_);
+}
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
new file mode 100644
index 0000000..4c59c32
--- /dev/null
+++ b/rust-toolchain.toml
@@ -0,0 +1,5 @@
+[toolchain]
+channel = "stable"
+profile = "minimal"
+targets = [ "wasm32-unknown-unknown" ]
+components = [ "rustfmt", "clippy", "rust-src" ]
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 100644
index 0000000..af85000
--- /dev/null
+++ b/rustfmt.toml
@@ -0,0 +1,7 @@
+edition = "2021"
+use_field_init_shorthand = true
+reorder_modules = true
+
+imports_granularity = "Crate"
+group_imports = "StdExternalCrate"
+reorder_imports = true
diff --git a/spec.json b/spec.json
new file mode 100644
index 0000000..f3925b8
--- /dev/null
+++ b/spec.json
@@ -0,0 +1,176 @@
+{
+ "name": "Development",
+ "id": "dev",
+ "chainType": "Development",
+ "bootNodes": [
+ "/ip4/127.0.0.1/tcp/30333/p2p/12D3KooWDqY96nhSh1sahtNAZTiXu1KbeKiwsz9Rwowv6bBEUjvZ"
+ ],
+ "telemetryEndpoints": null,
+ "protocolId": null,
+ "properties": {
+ "ss58Format": 42,
+ "tokenDecimals": 12,
+ "tokenSymbol": "Unit"
+ },
+ "manual_mode": 1,
+ "add_sha3_keccak": 10,
+ "remove_md5": 20,
+ "split_sha3_keccak": 30,
+ "maxi_position": "follow-mining",
+ "codeSubstitutes": {},
+ "genesis": {
+ "runtimeGenesis": {
+ "code": "0x52bc537646db8e0528b52ffd0058244e043e8a45b61153204c434a070534c63d412249e4094823de82c3328b4264ecc48046290950666699ebcdae36560aa2dfe3f34603e6ef9dbf9ab708ca1987befc032db52b5b8cb9c6f74e54951f04ebfeb6b7dc5b6e29a54c3205701383118610737e4e6f9fe0943847ce915b2e03850cd52d39c130e927fdec04b939b9eaa12783b2d99a602885f5d46d1c5e3827d7922fcf66ad0eaa2def0a6f5aae12a00b21ba60b1186a08dbadd9adf6d960d8df1e9d7e5ec8c5b85bb43f7efa4d8f1f095992c4fef8eb47427ceaf3e3626d6df49b7d61d273d76098f4ed1e751924bd82a114eea97396825eb7a41bb5cbd7aa83a44b9ffed2bb4bb2ea96f449e5450f572fdd48dec87117817651972eddcbeae42e46bd5237ea4ae7377f4e5d060ae31c9b691d53fd752e7ee7d2c3ebd6af6523f3ce8d7a08b783c754fc8a533248bae7a1f5f1d0fbcd995f2c837c78c8facd47a84e443434d46ab9b358a1f721cf092d53fd749a14a6bb0d18ea7b3cfbe6d49cee030c593e60e87d18a7bc38f5d2d9a9f7389719791eaefbf8b92f9c4ffd7efcdb1732d4873ce7a5d7cc56adfaf43bbff8b1b33576b1cced179f3b0df82b2885a93eabde5ec1294c45c18f3fce7906409217291843598d7e6ffbfdc2a1bfceb54b933e3cb43f3cf4de43d6fb388f5bf9f29cc7571fcba01efff1b10ceadcc7c7328873962f4f0649a760d8e479c0f07ae637fb621c4f73a3f6a8b39e7a0f18b2bca7de81a1275fd43939f43c3e3bf0f9988378dcfb988342fb3cfe9d57ef79c0e9dcae0e0c4778ceb7daf31cc8dddaeee7bf7d213f771c509fd1ad053234f4dcad70fe0479e81900596852d7938a2bfd32fbc4bc6ce996746ed715252525a5e77671b742fbe17cf99a1d249dfd5eee94546fa75fc83d289ddba539f7ebbad62de937c2e185f3a573a744f3ea469d7dccadccb95de1fc0ccbc71cb479771bcc9f6e7f0343ead6a3cf764d30bcf2d3a35fc969dfec9674ee38c87eba511bc9faf1db8fbb581bf56c5775e9b65d994be9f4e39fcf5d463d72d9379fbb4a9d9fbb087017427421c457376afa49e1be82443668a7de4ec129dc4f70762b3a0349308a675bc210a538e7b2aa7d4182e74b145d6ca334cff09b2ba0a02385a9a71dd41cf142ea470232780184185ae418638507c513a5946eb4c929fb52c4cc9aaa13ac4415177587cb6aad4e70419d78e3f1ac3309092827cc8431040c6aa420c0549b3b43b001871c4314208d0b3ed284a993bd5a6baddb8a3b25405ccf9cabd58b6c692e59a2892f4aa0e0c68d1f086002931db38617a614ecc051c2a94ecf6b3d73dbb43acf5042cca993d99ad5a6a9a649c0c5094a27cc41061733415ec031f2013abed001468d1c16d034297501a28d3a34e860a60a225a5fa6be8cc1b559a283870e432c31075fb144741e9e58232a0f2d58ec310013b6700928428f24988e8823883a2c29690f503d48f530555956cbea2657748cd8d44f3f38363d434985794a635303cad8638b39646083c311302a803567d634a1f540068b2d98a6903a824c7cb2462989246ad8410b0d0108c01b23ca50a20717c030b3c716464c983161cc844113464d183661b8a04456dbb2ac08309b0bd366946eb469365565b602304b69d0d09921862d80ba28830d56f34314173928e110158494a22fd401bd88a20d1b2ce880e10d0030d99930285936de38c38c2d577461218cedd1b427dc585b3779466ada60c49c4d4875d3193710f1a529aa0e78c21b4b84682a83c39821d2e8d1656620f3061b188c98e2fb020b23ab3ae28cf1069d3b4978a147d20d72b44471c513687c38c2cb88a309292ace400307164094d8d2b0b075549d0d8aa1fab56edb139afaa4a63eb1a94f5cd4a737f5094ead434bb10b1a315bdc446921c64892292f5f842883c60b2a3a9bfa010e2a3030b4a19303055f68f981a28d1930c63871861c5e340b67b2f94292521a70944107531163bc14617a4399cce80250441a43a0b1041346d4e9364fd3afb821ab94eae073d615ecb0b085852e1e0b32b05cfe4053060da8a9a52d550c10a3062ca25eb062cd173f30c090c1d02483c0b054617041b6e1754726a09890aa9ed5329b656d32a629386a77268c0e65ceb461c41842c03143a4c91a2931a66ec882091ce4521d159454585281290e8e020c1d41a410c61e4f380dd1a2e95e4175911801c41b766821468ea51456504c6d4b48f50e523b4c6db556bacdd5d31eab8ac50fa82a67283b358d49c9322d592626cbd4e473789e50e2a43426abfdd534fb713b782cf50c9542172937335bf334068e21c4a0b3441b2d421e70861a69e87005174d5c71a782b183c3161cbae020030e60701083c3181ccc8820c51c534b384165431330ca7a2a8b38c2a74d0f347411049b215ec8828820da90654dd34265b554592d73ac4d0247191fa098a206346808c2044735a109ce0b2f382187993972c698a1696cc258cbc6e9299b323d6968d41c5bbfc439930798357a9062822f7470b2a60729dc6072214e1076bc5abbad3843ad80f2b83553926b2ba4b6356a8a789259b4ac96d14ab78d87a9ca839bca03150f552c1ee6788bce227ea03603a2462be28a1f6fb4b85952410f2f506d0861a28d3be69c80421e5ddce1a38939174a2a5a40072f94088385342e6c59f530460c3bcc01e7cb0c53dc913f74b07632245844a569430b9b326ff8e042ce1cd6c30b3ceea45185068d17490655551e75dcb0041e51f87c01070182b87091861c31d6c88007471380d0f420a234cbbed65ab7ad4c98324e659eca9c29035546aacc1425a2b469d21ae20b4e47ded810430e43a0c1860f1e131dca74874cca21ce147aa8f04297252ffc83131d3a56b4d9e386217ab408c20d3cb4b8828928e6b0930412f18e3d6186384b35142147d4173c6e9a5653defcb0b446197a38d9a1076b5ee62821c31b1bc6c0e9be38e2878baa3ab8d0410733e6b0c32db1003b4d26c4d0c3116dea7031f0c0250d0f5be29011790b2930a8e43c41461d632e53f1c3116b20f1c69a357238e9881f9a769062e1ce9914b6a42f669c720013a70e3676b49206516daa9a96d4e8f284d69243ede2af800a616ad0e3e9841cd460284d53d234fb9aa66daf59eb825307f1933de341b1a47caae566e8a90b54444f29437d26cb5c1b5f1366ad952f02d071051b2798f1c61b35306a9f6e2103c391c6134a7c5115449b2c9a688c7bd45a6badd46badb5bf463951eaad6aadb5d695576badb5d6ba02e27ae6466dd49e7c5d9065d0f40976bd71ca9b991b35cb9757a3f2f83abfd65a9ddb153f036b0557de4ff728e865894959d3349b56b7ed0b93a6cd8ab4030b4f61ecf0c296a6a423ece091840964d430b147a6c5844e8b0b33ab554b0c9a961bb22578a4d9a10e17348c60e3b24973420f46acf145ce13577b3114474da3450c41af1843b34ad3fcd05af4509b0c105eaa9af080a389062091841a1c9ed6a803c76441836a82c2276a090a4fabd9cb535252320166a5a6d530656dfd66b7a67f1122e3403b7ffae4dac55e8d1d05cb6a99ac52cc5307f1990eaacefd321203d541fca1f7b5b2ac66331e13ecccf082163fd0410374c21b20da08428d32e4cca7383673cd66b2882d2b4f61ce1c27d8a023a60d573104115ab050c10919ec10e372ddc1b33ffff63050e83f9dfbc51d149d3db41f9ddb45c130fee6a17d766e57e6d467bbac53b7ed0ae357a7ce79edd29cfaaa9dfa4fbba453772eb24fc1500ad06f3ebb15fdc62a2f3a774ab85bd137d0762bba5173bf8cd040c9207ecdb98b6920479f98972edde2b76fd443625edeb010ffcd3efb71dd9a1ea7bc1b39962fce238fa91eba78c24d0a6cc0a85bf9220198363108d1e60535ce80d1fec2467da2b381a114a2af6e41ae5bd3b94b9281b65bd32bc8ddaa6ed4466de56bd541d3a77bed8a4f9dd52e862ae2e9a9ffb44b3e756f17437dc9e1a903f58b83513fd2af09a3ceed8a5fc1d07e687f3acb177b81860833cc9c00668e138cba95af2a26c8e9c20b3266da9002464156b7a6c7292f5cfdf455bfa292929292126c3ab78b3fb41fae7e3a034d308a3f0320298b130a4e860a6a50b37135705e38262e060e866bc6bde3d6b9765c3a2e1dee855bc7b573d1b86b705a3816ae1e9c0a770fee046ec9b271cfb87968516869b4280d4ad341cb41c3417b426b42734243a32da101a121a199d1a4d0d46862b41e341eb41db4233423b422b4278d08ad8c46461ba30da109a105a1c5a07dd166d064d0bc685d3415b416ec1d4d8b6682c6a429694b560f0d052d056d8bddc3d6b16bd82aab869db269582dec16b68d8563c5b06b6c16b60b9bc64a592a6c9485aa715828ac9a8c857a8565c22261cdd814ac0a56c92e5916b23b3237b2363236322d323132303232b22c32aaec8aac8a8c8a4caa6651b1a870aa14554e65a356552dea16f54d1da342519fa84e5434b5891a445da29ea94ad4242a1295897a4435a23e55226a994aa68ea94354a79a43c5a182a934d41baa0df54b9d21b362cad16b681cd48d5987f432599032cc2e9408da0695a26ea816f406ba454b516568a9a6828241a9a06b281bfa45d64246872ac135d125e811d4886c8eb944c3d01d280f14073a359ba05f2614d4cb6482c22077985ea61c198384a18d9829442c2612b10d15621e61edd02b260f354d5635d150273a89a9344f9077ec10991bce866c8c39439646a6c6849a51778ed944b3c896241c3d05f5411641b19047d036d91d59c66e91444c2966145688ac8e6d633e4183904d370e8b82ecd279502ba452d79126c813fa0efb45e351a728183b85f58256416da03a7415d38bb6a2c3a03fd033990a2d437b9970d813e297d8144d9064aa9769029da2a166988e6a28ac141d45abe934d1857842d4427388334419a217ca0697021d33d3cc37620c3586cac47dc9e4cc39e61c4a86e342cb4c1466125a0d1a0d9269c631cf7032705d26173983b4236b3357a828646cfa883633e3482aa69acaa5a65061984b4825a613768c25422e4187984a4c33524d4607b765aa20cf48212c1959878c42d6d156b8773814b81568940dc272a1623412938e2d63f39027dcf81c2b0b5acb232b4e3ac06404274721306172a37c32e58809934d484852b19523119c4c61424496c14847568eaa8cc004e9e8034224197c84e404c90a932752a080a00356a058399a72a464881c6348be9122c5090852a43871801821207d60ca919430f8e8c8034ca82031410ae103508e441024c1602b52a038413a7ac2e4e8c80303906e988a07a64061524295231090c004ca088f34e54884129c74e0f20b3e02610a95294c9c308132c21f8130858a0490a680800a08207c11e9053f610265842f01490a084080e2010f40c98025bb78420584a3274624176ca584a3284cac3039427252e568cad107985829e188832954a058b17254814f6ec122604285099411beca51942a543030058a931040f08007967c520b3e4272f2048a1427216072048223104cc08327471ff880130f0a20b3602b5140380a018429b68234e5a8ca510853a0b8c4620a140f10916d18042856a640a98274e5884909524618c109932a474fa44001012c0a0854a4c07ee4156c658864c34e98fc482b5804264e909c3c71827414011f59c508a04871020509ca142840e41a8e020215291d6072840402108eae1cf990525c858a03388e47523ae004690a083cc0040984a32a509e4c81c28489100ab238d91c29bc543d275d865252fa98248554a9456a8923c46cfaac7ce69c3e73ae563e73e5e3c30753e6cacd9336b3642b294bca719129a5946b6c2a2b8bc5d26cd632a3b466565259a9dce82669db2a2533d35a2d33ad9ce51eb2b69967e55aa7262be59e75c6586bd566adb5e36ab96b9d9c94b2d2cef2d4dab45a96965bd64e4a2b2b308090e5ae922b655a2b9d927665a6dcccd4b2ac9563b6dc9123b7d56a95ccdc525ad949db9c8cb4a3924a4a9b796e32d62375d64a2311da52fefc74d176b5b6a354525a6d2d0001e6eca993b2adb3d68c29a541b6672532ebec69cd4a0750059045daddb4bbb9a6b1bb69ad1c8f94b625677572ad53cacab54a8ebb878b5256cacc1379380043781a4856e66d080098593273a4b5f654ae55ca596bad5c25a5d232b3a4d552090691525ad99b8f6ddbb6ca552929a5cc2c25d72337592b4fe58ca994cc1c2533cbacca8c32d3cb5232cbcacc928747565969ad5556fe6865965c5972adb1da3ab75867ad1cc771cc5c2995b2594a59abb5d6522ae59cdbb66d5c654b66e666e68c6564a6b4d64a25959225951c67bbebba4e4ac9939b93e3b89ec9492aa5a45272524a1963acd33afdf1e3c78f4a679d929394522aada4952995959bb2566b955c6b6539236dda9c0c1c0dd207770955a8887024428d22301fe0a88a079854a182e404042a52a438f941e22a72858a0890287025c002f72c384008569c84e09a94a3101c496172c58908467c7c808a14261f9802e5031fa0224548152748560610c48a102343a280e0a40352a450a94280098ea23041ba72348582a30f08f1ac20598172e5284302c18995294eaa20314182228208545a14b005503c705465842a4e8ea4804024c8932947238ce084c9d1079c54a1826465023ec091085090a680a00a48e4012410140172e52804264853a0541981090856a088f05304890a952a4c9e1c55397a52850a1357022c6062c1511427523ac0e4084a15240258500500972d787264c5ca51951144a00202932a4721a002c25115264eac3c01e188c901a2b81260c108201c59798560ca9114c2076032850a142b558e42c0a4040f40a9721402260a3840021260c193232654904628a10a084c90ae1c012004538e2411dba5394b96ac09d76ec249591a4b92c49234b52649314b9629936a6d9214b364c992b435e1a4a424db84b3648949b1499698254b379624db242966c992949435498a4949dd84b364e926596296d884b3c4269c94254bd26cc249596413ce92254b9624da242966894d9262962c494933296bc249b549969894d44d922227d126cc4d9262524c4a4a924d3829a99b70525252d26c92149362134e8a4d0ed0559199a308569c7079c0d823947cadc4785384963b33341901638f67d8e3937c2d11632708a82cf6702306c63fd288b114c4144d542401638f61d863137b6462f9ec5189fd0afb08ed42c0b387c0de843d56b54b7bf618a75dedacc9ef9ce2a1c36b08a85d9c536f69e171ea4888b478ea18f48ba1a867d0aeebd435d802a3de41bb7e38750f3e907aea1be802a3fea45d3e9c7a0823b46be5d491aed079ea5868d8d22e204b13c8d024f5d423987eb99292120c30ea514cbb7a9c7a74eaa7760531d3ae78468c31d0b0a0a65d2ca97e0d4db54b88538f5c4437673cf5f8a60518f508a75d439c7aac8a73da45c4a9473ab10e1c4f3ddae9174351ef2dedb24ebd97989aa69e7a83e91960d45b4c3b3df599169e7aa3e92f30eaada65d9b536fa97e79d47baa5d9e536f2eda8d134fbddfb48be7a9cbd152f49e37d5cfbf5cc5c4ceb36a5cd7099b2be658228c26d4a05344d585176f30f51046cd6fee40edfaf1cd5bf2159986dae5e39b232132e137c7a05ddb6f6eb4b906db05bf6dbe817615f1cd9bc857a4da369edfe0b0e337ee370bc061840d3ecc41451c5cc036d6e20a3bd480c9630d15bc806d1cc56f1e99e42b2a356d467cf328a65df3b7375878235cc08c1871ef8d32de88af8cc0a1c41bf11f23fe5dcc88ab91c57b3eed1a41bce71c0a30cf572c22def31f6f8d3dde7320af6aeabd2698e75555bd27f59e1bb1f1f49e67d02ee6e23dd7a08d2def39043a681743b5f1e63df7c0f30fdac5506e10f19e6fc0f3267196def327ed6237ef7908ed62a83854efb994767defb95117f3fa8b48b1020f2e4f67f450c514ac481e64e04c51c70a2db4b10656c45945d4c0e28b7803ba58111fb768b0791fe75480f9f80a8d2ede678ff781b3c5fb78f13e6904f13e64603e3e6978f13e5fbc8f1a64dec73368579033e6781f674017f3f9f9f9f93943cefff802bad88f63d0c5b63a9a78cd354d7396e6ae796ba85d3fae39923aec786d82d7344df30f36d02ecf356fa27908ed2ae29a4bd11ce98ad292e6e39a46816b1ec5689aa6699a11d73c4e69ee9ac737765cf1eeee1774313762c48811235ed4c58c5060470b4f812ba08b51607d7c7c7c7c9c48e88bd451c517f157172be2799ee7799ee779eeea629e16ff43071bffe309e8623f766699f3a50a38e438038a09b6cd110397281fdad8a07406b6396bdbb66ddbe848e33747d2c5360474318d0e2d5b1725d694f1c31033f4f0260319d684f182aa88263b30eb2ceb6ebd3544d4c25bc7a05d3e6ff740838c39527138a17103665d03f9ba600827346e9c00c5431798f50edad580b7fe81f526f2d530eb218c20e6ad235d515a626a0a139d9c78eb714c7cb21ecf588f50f1ad8fdb08b31eddc8d7845925dec77d7c7c7cdc822ee6e325a1a9418535d0c479a20a3a30cf0a1382f07286871cf07001e631e03d3a7278cf0fd0c5bc6ddbb66ddbb66df3a12eb649cd3d04d0a1247fe8e8f19a5760802e66fd89d069f3445ca88b11b1dd0f193264c89021438644fd10673a30bcf579a48b59abb9d5dcd3e8acf09a17c009d0c586105df142cc10e385ac792167887921be6a17035e880b2fc483ba9810d6f62cb7f245c1121c1de8344181c41430967bf255240e0a628250028eaa27602c67b56b01cf726fb158666c79161517aff96cd7d06bdeea629a1d401763110511238a0fe202e86241c250e27b7cb60bc8f7f4f4f4b8f7784bbe784e4ff63d623c7d4f4f121a2beea8630919d6c00006acc73590af2651e041039c2722da40c17a30f89e9ecd7b867a3c847679dee352da25c3d8e23dff01e679eeddf19eafdaf57acf03d0c53cd6e676732fc86fce6c7e73a02eb6b914afb945e1b5346048bd0003e38bd71c680c4cf32350616879cd91b40bc96b51af3900ba98363f38618e309e5ce84169ce08ce4c6186142678a9d2e382b76e9c78eb6017b3fe75b19e3913bc21e22770ef621358206e81b80784c80301e240edd21c0810206fc23c1038defa6c97056fdd4817b39aa6699a53d0c5342fd2c58010ad76f895ff74b1955dbd8f2f5cf8a208288660630c1d301f7b8011e70e36b668e1421d980f67b5abe87db8b74bbe0f076a97751fde925a603e7ca85d9afb7024edd2e668e17d388ff19adb20078c395359c4b1036ff19ab7dac55071d079cd8fe800d31c099197d71c83767116afc901e735cfa05d41af39912ea6d922e2e061460a5438c1a50d98754fbe245027cd19377bc471e20e9802deba67f1d681dad56fbd657dc83a51bb10f0d68db678eb19b46b7beb1a58efc0fa07edaa6fe558f1d68774314b25a48bf978f33fca88f33f9cd5c57e8c41c65f9fedeaf998b8030715e47021e209a43256dc61020b62dc9006d15f77a0766d7ebd25ef7524ed9a7f9d2885bf8e41bb78feba91061db4ebf5d73f68723d8476f5f8752923c4f9eb48edfaf1d7afc857ab071fca8861451e552cc16ed293165b446dc1218b292c60d7f5f77a7eaf8f98e834c65f8f63dad5f3d7e3937c1549c1460b165bdc602307d8f57846be929a90a34c1874409d89420bd84dc05f8f52edeabf7e3d4e9d00bb1eddb4cb875f8f6f6e194befa38bf7e141ba988f32b6bce640ed9aaf794bf321cd89da8564822ea661f13e38bc8f03e9623ed6732b5f146843052832eaa041872798e79e7c3d00052398dec82047135e609eb3da85e43d326678cf575dcc1bf23dce6f7c8fffe8623df60698b56ff478dbe6ed1856bcf59f76fd787bc65bf7d1c5acf69b5bf98a4c6b8ea8e20e26b22803b69901c51031aca1458e345a806dce6a1702de80f19bfbb8d7c52e15d4f3e811c3f3784f17e3c1638ae79e73cbb9c771cf71dce61c676786e77e3cb71931870e526730a9f902e3180ba92a35dc7883c61166609c05cf71be81765de7bc897c3114a739379fd3c384e77c9e633b35e8b086147544ad80058c73b1b1b4254c991bf09882710778ce231337c459e7e473ce4e6fdd5a3b59bcf555bb86de3a4f17b3ac20609ae69e1daf45bd96c799d7fca75d3cc66b78b479cdbb2ea6f9757bddfbf9ebd7b92e7601f01b1e3dfce6b78b6d5b17e358eabb3bbef8ceb52ed655f1d96761d040440e5a7c985ab3042c734fbe9270d80107266e48010713828065ce6ad7d067eef215955af2e5653ed42eeb9923216ae333c7a05ddd676e94b9069977d02e229ff9071b6897e6993709a15d9b672e65847631d41d547ce648edf23ef32bf29524441e5384c1e68e2a2f5680651e95e4ab6e31c10c34496cd1e169a95d157ce69129cad0aeeb99c72699790cd32ece338f62da153fb3e3399fede2a9e7dc7631ced6bf646851c6a9ca081e56700276dd932f8e2306a63228a8b1638c09b0ebac7619e0af18557f3deb62d785fc56278bdfbc76b1cdd2d7dcca57921065aca024c4162f4a5801d3dc93af21718a50d3020d69d84903a639ab5d42af9171e735a75d4cf3d92ea0b750757e78eb5376b1cce99bea3bcc122c7a3bf52b2a453a1ef5d0fb19e6a347ef31eda2cdd4ade861da353d7a8b69d704bb8b047ba9b2e0593df882e6d25cacb5d6dae122fbed764abe7aa9976c2f517dbb555a6ab74ced02fa76dbd46ec3b45ba776f17cbb7d6ab767da2d54bb26f8763bf5ed964dbbe6b7675d7491655996d9e1a2ecdb33376e323754df9e39654fedd999f60caa5d147c7bc6c5b7676cda75bf3d6b92af3867ce470fb330df9e29b5674bed195375c2895a6bad76b8a87e7b8582aa61a5faf65a6b2de3db2b9b760df976aa075f107de00aa2777f187db8f3ed94526a878ba8931375a2134ebfa2d28cd32f86b54f3afd9ab0f669878be6b7534aa9d21ddf4ed9b48bf5ed734abe2253649a91694a59c70e174926a709c3b74f36ed0af2ed124ebfa4c491724abeaed4917564779db6c345cdd4d42ec3b44ba776f97cbb944edf2ed9b4cbc7b7379c7e41804d9c6fef29f9fa40064577f361537d7b9c13ebb4cb0e17c56f6fa5f65e6a6fa67681dfde4ded1da6bd9ddaf57d7b3fb5f799f6866a57d0b73713dfde6cda658fc4f9f608a75f47886218dd7c7b9c922f223942fb152c5ca4d41e97da2353bbac1354bbf8dba3549a6f8f6cdaa57d7371befd49bfb80e8ade5421bbf9f60ee48b2583a277773babdddb5bedeabe7da89da8dda85d47be5d8376719409567c3b04da55bf5d3ee185bdf4d125125e18c9f0c208f5d1e91b2f8c4e1f9d8ee18591e9a3cb20bc90e37c74e9e4855ce7a3533dbc90d97c746ac70bd9cd47a7505e78e4a35328bc90e8a3cb39bc90fbe8520e2f647dd494be9f9e0190e4f1024c5cae6b780cb587d457f00ca547187f7b3267ae22658c515ce8449711641692f2ab6ed497bb24f2338f9ffc6ef611d9407ee6f233d0596883cf8261f45ac6b443ca61219ef352ca8f5d1269d4f2ab2011e9f62598e44a835cb7d89b06cf7f3ebbc7af264d338c21260e35b8f801c6cee29708d6a02186a9349660b20363777e592ada3883c69d35b0f802636ff14be8072a4ea8a14e0d78d081b133106b511a41c68b3edf10c28bce6ae219c67eb98af156cfab37ea9e29c68b3ee4396acb16cc12a0faf6eb437ac852431cd55240964930642c988ad7b6cb7197c5726fb58686e414d1e3a2e8ec377e3ecf0a6290033f5d4616aacedd06e04ffae3af74f3ce5d129e6f55252f7ac89a1ccb57e6ec1e17f53c7bf67932a89d3b20ec18afbf86597ebaa49cc745f2fbf13772f2e379223f002bc8fb9e853cbb0fbf581cd4ce6e845fd6dba3937c59700419d49e79d800ebe1ebc30a360f07b059ab799e65b1e42bf38f4e23b050cb3bbcd87e2be771d18f3a3b6a3ccc42354a973edb452518a99e0190a526d1b667dce837c6f8cd6eb17717e396f287355793d2f8611023ea94f423612125fd918bdf502ceb94f4b75888fd5ace78c2a427b281f621093e7a7f88e5a3cb2ef6133fa02ee6b37bba9c60d8fd7946cddf5f74fa9190e5dbdbe547427ca0f8b99c6178e1cfcfee876a5cddb8acfbb9f7c68d274c7a160bb1c7ef48b7d865176b79b77e64a7243eab5b3de36cefef087a2cc49e442365223ecfcbd387eb1691e7282e637e252486f6d9c6565ebb423a84f3b05dfaedcfe3a8c79ff9b1ba155d7ef1a39217ae3ec6e8fdf1335004a338119781ae94c2c60886524a7fe9310af317fa3c67525613bce8dc92eb272f7ac87d7b4be76e450986f1a547672e8a1f4a9752dec9493cbce81ff2e5cf762b3e7b04adfc2e4f29bd5f826195f718a83bd2136e75060ae3d34bbf318eed2f658cf3c1f36f0f23d3f7215731bdf410e4eb3948fa9a3959d46130948a9885c2effd6367c44263bac5d427e6458d9be1cd27e6454cb7d83a7b183fba51bbb59ed71eb23e64a88e2c799885622bbd0535e89674a32f836e49ef26e2d2b377114d44c8552f3d847649a761b027792143bdf4e94619b4cbba740d36bf9ba55403edc3cc43eb3423420186718e85a160255c2f81739ffedbb18b714744737e0f3a221ad80107b20c7aa3de3e221b4c2525faf3b9db202a29bde6f6eb670d64758be3675ffcfa85feec977e5142201f8812ae6f7f23473ffadc69602a2919f58d4436981f4a19fa26dd62af6095f95298ea3d38c06bd02d76a3ae2778d3afe4e827bf3b0d442525d875d979c05030faf5dfc9d1eff2df26b241382bf841b7d893fe4e291ce5ea5765be1456cd2c57bf70be07dd62bf6cb3acd2293b6a7c79c2a4d78085d8eb67ffb2a41d99f29010ef3c910de89310ef7cf5fe0a8655de8885d8bf9f5f38f4ec4fb639aadb13bdfdc62f668482223f44860861059900c8ea870f1faf87a7e3eea6d9acd2293bb2503b7724c43b0f8474a3ee96607ffc596252779721dd636899d943d64b0e62971e9f3b991463d2f589c5c045b7f8c3e8d52928bd9d3fae5bd3d9c3ea5efd567978d1ab87dc92e8512a68d44d8417723f3dfcf9e93fed9ace6a57f439e7bc94d6e6e817c3f9917efc32c63c46f8e9def34bf3ea965fd62b1846cf9cbfd07bfea6f7d19b6510bbfd580685f6994630ac4e3d8c9e81b35bed37cbc3ab1ebd9dbb25354a74ee56bb5153252f9cf1b3dd6a9f1fd2fce846dd49b4e31d4a27ad734e4aeb182bcb79ab95985abfd95afbfc38793db4a117b2f8e54d37bc8ebbfe356cf2d5b72ab399d52ccb6a0622d1af21a04337ca35385bc130e9abcfaf1b0cb95b2143b919f348cd81fc069985e6fc6abc93d6d6eaa77372bcd05a6fb5a2f4572c24bdc9f0e644c017366aac563fa51a9781423927068617ce8f7372d3a5876edbf0c23927c8c539ad340109933678b366b5cec9030c4b3f63d329e8758b7d82ab6eb127d9d83f5c069af7dbe3376decc6a0ba7de371549731357ad04ab00a5a1934db153f1b86c75164d87c68e4db3da900eaed967e56162062894952f618d5ef8c4778a194929a57fb69ddd23cb2d872d6630bec918bebf1ec20e0977bd02f231954ddbe69669fc2453f9c9d0a5f00c4d94960d7caadf0380abee0d959f245c4d923c0459eb34b205ee0c3d93b68978fb323805f4d6450f51fdf2670f6d6112eea9c5d882fe871f60ad8c5e3ecec6d3dc6568a61229492925212d6af563b6f7529a77df7564efbae7397b9515bee62f5fabdce1d77ebba51d72e76fd669cfda88ce4ab95c2c8a0ea2ccf95a0acf5a8be7aecae7397e48b4878d1edc7e2a2306adf8d31d3beebb73b213121ae5d4ed33a4e8b7f6fffad1cbd423efe2b5310c26d9fd472e9047e2bb76d6ed442bef8f77e52cbf5c1452af9924b4af2d555dc61e4aba538a83a15d512559552951287e930dc52d56f6bc95acb8fd6c29d92c894c417a12954ee7e528b579fa3989e3e4ececb9ad02e0f0c30643f7e0059c92536da103b7e689ce7f9f0a9aaaaaa52b3b1caf0eed5b8ee936b58a8a58238cb432f88cb170560105f73833c473149b5e58a7c1136e49353324806b17ceb48e038215fd64d20c28af8e5b88d9be0bb7ca9f0d8a86fc7dd4fa6d0e5cdbb51df8d13f2c5efbadb75178c663ceb423efeac2321be10bf2c83b89f1cd3ad1a5f3adde0c9290fa51327e4e39752b60ccf7ac8bdf5d5aa5bd6ad5bb0a1ba559db7acfbfa857cfcacafc8c7b0205f683f72ddd7ef7dfddbf7e36b29168a68bcb0a1a0d4dc8eb3137c9a740a235f72a9ab64507527252725272596e7c9a530d67aac0f3b420df9b6fc380a0e1be244be2d4b0e1be228388c48fd1b3d6e9e3003a5d427d9300164457f3839393db1f1e1333db92497e4926452c33d9fac2256509de7936c7a382bffc64e497f0776957cc9a90eaa5e9de3051c2b0865158b55f535943d2749fc353f9f9cea045427f2712720946fbefa904f72d141354ab151739f9cea56f59e0d94da27d974ab7aa4425e1f4efb240a1e47b5c0f41cc5e4f4fc990f17bb9806c6293cebb6a5ba553d8bc9352c543b29c542d5e3192f6ca9af1e4aa9afde52cd868be2575ff9e86224c43b7fdd7e2c2eba20104efba2111e47bd00e64329e7ab478f5d12cee337e72e098bf19bfb705ebbae5b4bdf76311b3f56b7aadfefa75bd5bae69a6b9ef9b59fe6578bf782a1087e03998542ebd9120df4b80a46917356ddf2d250df6dd45ebb368f1e27f8e89cec62d16edf181e910da25bb7ae79685f72b28badbad51eb6680ce340edca9c9b5aede24e498c91309594bc3cd2e6f1bbe320c86f60288267160aa3f679b72c0804b6aa3e6c7d189fb22f72d1ada96e753ce32c9fe493d7138c4fdd6a37d3adf628c66b0f574fdfeefde25eb5ff1ce1a2a8d45093cebd97d3348d73ed5e0e8c725dbb5aad60a77dd52ba8714701866de1729d07e49c8bcba0eb1de83288f38fbb95a593b6be732e038511ea65fd326a67d691747a4ec7e9d6ec2aab5910a9dfabfa49ab9dc043d65730e47e78f595fd6a1d0810b73ec1c71c64c1ea40bed5f7e3b39b6bd57ae54024faf3ba167dfe04a35c70d28ff43da71b18c5f3e99ac76e030f8c129dbe0646b0d5e8383f2d1826bd1ab1825a29df38bbaaab64d3e6a1f719f38b480621d15623f3ea0d275e40d56a745553b51a55757ae60da7a95a8dae924dad06a54d455b0d22f9ba2283a4777e893690e886442ffd7a5cc42f592ccf7bad490649d7a4e6d1ce6396b99739cd588b5d92cd33af3e7e9da577ce9de6990786ac1e30f4be6a3c600443fb9ae67cc57a078696032f18b2a4dfd9ae0a4a16cb56ebdd25d9bc7624c4cf34ab59cd43d65bad3f033d1692dba77da1cf4bb75f183f8c4d1f5b435dd56a40f5b40f2abe5cbff8733aad4b90c0f9f9b38bd1af560b90c82e5665178b6c81c72e4691b0f350bc805f3a92d8c524c3a91f7f7356bf3df429a8c17c0a66209f8246fd14c4203e058986e205fc1444d22da97988e4c3f6e9949d7aa8390543242f9d7649e2d75073493d3a1736c881511a0ce35fe718446a0d44ea495fba05f1820b46d19cf3a97173e82318c110a92d88154859ed9df30b634cc6abdd1beb17cedb6075a3e6beeb4692a1fe3ae7465d9dfd82dc2d8d0b2777bfccb52e769dfbaedfc8695f385f73aebb24996bd58dfa7ed372deeae36e452c5f5d7ee0a4d425120b6205d2a5d72f4cfa3aa773474112e44fe62dde342efbf856dee3e35b6561de9fa396a0fe4a8e87878787c7ab4f8fb15b323d9f39a75fbeccd12e966559f5cc48ce2ec6d3b35d3c204f9e737a77b1997dfccde6b85b729fe70be3f37cd0ad69d43c1f7f06723d6152e6757e00cf512b4c7d0a605ec873140a5bfe4a8ed69974e49c8e03c6aa6ecd0946383c753e523ff5ea210f58c124f12310af60d87e9df9b5f2d0f3e9d57bbe90e17ced71201f77508f275f3e5c76eee3ab8f39c8070c91e4f778f501d9070c7d3cb40d72eed3f9f5f9713b781e8824bf39ef3cf3ee9444a13ebfc128dca47e3dbc20f58cc7e96751787c7ae7b1db80078c429dfeece0fc74d92da14fb3aa9f6088d4df03d65a9dbb58e7dc6970454949e984ef36a3ce321a5b9f3916299f85f0d9e43b77eeecf1d5b32ff40d561f4af9cc8d7a9210bf8253569f8151a85be72e0975abe4d62fe480587218906e023e7ce5dc81b65b9967599679e65dd6f30101c46fa45f76571f7710f5fad96e5107f2711d443dfbc255cfc7e3abef87775ffd42af53629dfaeaf3e1f5eb3cfba809dfb904f26d21c35dfff1ad9ce7eb7123e9e3eb3eeee3bced5b75cb7e7eb17ce6f3038d24771bcca7f18c58c1f4e91975eadcd16eb16799493dede354e7b9a76ebef51ca5c2d35b6f6777db9cb3af3802d63330dcc07ae699c76e89f5cc3fc8fcba9134eaece376f090faaf672e379f9d12ea178c92f97449edd65e66af83f6ba74ea1c92ec4e89f528178cdd12eead7b9b26dd82a195a4cf40d0a97fd0ae2f9e112b9020f7d67309cecfffc2e32815aafe7e5481e9879ea396a85e06353c8e5a7afadb439ddbc5393bb3dff8f5dbb937d0a44a764aa46fceedea8c3febcebcbdbb58cbf075b10cbc43dda25e3f24dda22e3fa26e51f7f1b16410f506bd5bd47f7c401d44fd7295c3e3a815a03e01cf512b2cfd8d5c7ff72275e6ed46ad69dbb66ddb96f9b66d9b73c741b779abecf3fcc68fbefc9872dc656098f499cbef0be7c72ae3ec62d9e69ac74f82e1fce8da178ee0a3d7da2ea3cba5ded24ad1b568e72518566f8f5ffded0baf3c09f28e9d971f92407ef5b38f04d929a11fedb4ab7a4bef50ca23b59d6f0f5bc9a5797b04d39987b1498c0fef0c0c49b0418d1f8f748b7accc1ebafbefcc2125efae4b2afc7e3379f729acb2ff45e36962b3544fa391f46dfba0de46ffdc92f9c2f3df348152f68cf4096f20b635524417e187f33eaad5ba28161ffe61de17c88d479f4dbcfe321527f7b86d411ce47e7d9af1f9e398f67a0ece883078c227d6b8fdaeb47267df8e6b2fbc2f89b94d2fb0be367d1e517a96205fc3d5f78e4a97b5f88e4293f7711fca05b9de4cadd9c8b91eb62e4620746b1be79e4c0fac645e7787adcdbabfbf8f1710775ae7ddc2dcd2ff755bff7e3f7be2131cff3d1b7609a5adf9e8d40da8f7e0cf3edb1a9ebb7b7f8e5e32d8513f3cdede021c98f5efdc717c174506cea56bb8fafc541edda083ea2eff9e2d3f37cf5efc74e6286bedd7e61096ffd963033f7bed6f77cb1e979bef9dd1746a66fd7be3076f9f6edebba25dc473024c17bf59b71dcd77efbcd67703e03dbef10ee074f0af79bf5cc4329dc6b9e8121bff9f621dc0f1e92fc0d8c62bd7ae621927ccdf9e5e399731781ea5af68548f22dd87e7bcae60949f0d663b7017dfbd5e319f102feea5e17ab703ebbc1fd0ac6336205edf3311095495c4f50c49569ac1c237baa966e8f7ebbbb47d2f1a23733737fa17dd99167fc06c3f099853829c65e7339f6482e5ef61cb585ceb7f730173117d5678f9d929a871955743c734b7631ce072ffa6d2e72117d76d9713e7871769c0f5ef43bb9c845f2d969c7f9e0452eea67af1de78317fd562e661d05780e23dcbca1060b45a43903c61c14fdda69655b6b6db4962db535084f88e5dbdb85db23fde3bf930b2f3e43b941f3dc2dae5bfd24c83d945e82b65b71c943c10b198e988217f215cf5ec60b8f3c7b103141783e62b067784ef6396acb0a1f5a29bdbb985c59166a094e1fea73d4962d1f2f68a56fef3c3e9c3ebb25dd7e510e2f7efc683d59c38f387e442a186a8bcf739416aa14a25f373e5eb788785ac47c740834b3f4610f7f3e3a9137dfde41bba2f3e7758b880493f44be728d2a8797608b4ab3ffa4fbf08f004d46a57f423fdfa018b8e640f153e3a1114b0e846633e7a06edf2f97802988fee41bf222cbafc625ea8be580c4a5f2c862df2b5850c0a0e23c3557c8c7981c3ddc5bcc0e916ff8db3394e1e8e538ed4c3934c784ca5092a76dc89295809302101f6e2d92b93f1ec538d676f67af3ebd9da2f1a263f1fca3bb7c0971107b06860978f60a862d0a860b78f609861e183618b2acc78aee1ffa478f6ee5cb7a74e7d71037e4c4d08616304e50c0a2b7f8e56d3953859ba522b8100316c5853637cc41869d37506c408e1b4e6bf830060d3460d1b5cf8b5b78f1a91c4f9ee185f185e7aca91baf25472268c10211027873aa02cf8793053ccb441047c61254f621c7f5f06b88b3201ccbe3566bbc15573fa0d6cae3584e34c4ad7eb2363e0482aa2d570812f9e5f10f7e19b177d02f66a2765567c7a05fdafdd0f3d43c23d1a73e3d4ee9d195e8bccf3394d219cf9d924985b79202f499673fa20f836e45df80379fa394a6907cf421ef3e4729717101cf32017ada17c0abeff58ba158f56b752bbaf61de9a0e8f463ad3cf9fae9a0e811e8a33738a53c0d8c3c56bf1816ddfb3561d1ed198f7ffe8fe7a8139cf8fb1cc5858a0f5b3f73b80c147698977ee7a8a232b24ec572e3493194b7582a36531cd68783a2cb2fe8a395a397a292e7b94bbfd2ca8966e2525c5a8a4b6fadc7245f7c27275df07a8ec752c000e7c306f372ea432bbdc1b42b6ca6972ebd99bac9b64b1f7267b13cafa387431fda97d143a20f6d9444532fa7f7cfe81d464b43f5d26f84c16b7719bdc170fe6ca6ce607fb68bf8d97b09af7ee8d567d52ddbad66aa373caf59453c20cfce61f7ec74d258d927f30a72f49b3ec3fcf4d8c566bbe247e7ce7c748e517ef159be220745d7ac8f1066a17e066ad90414f2d433333333f31732941c4a7041e30c1fba58610d0c50c951040f5cde904385eeeeee6edee30539bca859dac2881538ca8d295c7822d3c41c14ae7d8e6ebd90c59ded1a3cff70e8a30ff1eb09172de8b480c6cb154eb0e844fc4ab2c1e90d2919cabc8006132cba11bf246066061e4c3073471c3060d135e017cb51c208385c8012630e3660d1796aca711928bc2d84273dfa16af9b74ab37d0ad2621def90f26d0b00825833a5ec0a3f4eccd45f1d93de9138b814eb7acf53c16cb7d7ad87a76962f769e4e78e14f3b774a7cbcc742ed33c6f6e122752b82e1853ce723b3774b29bd25c82cd4b35b11b48d86d73e8df0a2b7b327c5189fe61cb72c4bc49ace73a4a88818ca23d55bd64ff7b7d1e9ed6b4a6ffbe9a3c734f8825ee215d678f60fd83b902fa11a5260618b18799c210563d740beacfca0859d1eb678a2010c18bb113b917c2599598a430b145be4b1c21b18fb907cb19a1e96e8220a8e295e7803c6de72961deaec914ebbee3315cf6ee5cb8a19289c79420d1d3227c0f889244c689386196dd2bc81b147aa764d21fa28d3abb347385c349f3dbe69d77496ce3cc5b34729f91a12420633de8439014c5cc55574586bc245f1656b206dbf92c0989186862e77d81025074cb6f1a28e1fca2051450e60c0a4b3240c6b5e4a3094c2555f86b4ad81cc212bf43430b4606859dd6afe70f55dc1d05230f42608932e77486af7603688850c831eb44b108b742ced49485ca0047c8112b027bd3d761c23ca0006f40f99ceb7cf1c3c19be1d094bdc12bb442b83daade7b158ee76ba740962913e414bc403ead32788653a0593be9015b9254f74a3b6f265e41135518744df5df5ed61eb43fb2d5f0cf358fea11d226a309ebf6df7da59fce2286240620a366578e1851958d3d0c60c18e4b820c7863ab0f6293f2b83d8a34c8a4d9b8ce48b082cba06f24584412213984efd02ed122c015802321e486f90083bc3a897a07d82441824213e3bc3a6338c3ffb793228fafce21dcf3ff4242e2facb104154f5257a4a9a0cc983253684144174d30a9307adcb1470f5f307da1f4830f6c1861c60929e0c0a2cb8edd49dcf8a40990778b8584bc6521f6a983c71e02711a4744f190c69c3c8250c2009c3769968640410534435c7cdcdbef2e66bbc55dcccacec6d005e8bae10db56b56cb7d2192eef945e9f3e3905039c1fe420c3efa8cce455f45ff89128736ee57b817f9a24c17217620c20c19079c31c50a68f0900595155060b0d1250f33b884610406ca78634517519421440f6c628c1c46137558f0e2851f9a9e92764843c78d99378088e3041b442863049c2374b882cb0a10c0a20e2bead040c752135674bb7f7737cbafb81fe7ade14dff154b217ae9517a76e7ce9d3a3f41203237ea18104b0ecbc02c4430321424c2c1fc1946a4f8d28dfa935e821ce578a334c40b518401878933a8e258a3852cc66c91030e20def4b09d6b780e8bc848830eda3d6857fdf60fe47cfb06fa95c1da9f7c48e26720120cd87a5feaf8aef36a58fb1190a85b11c9c72024187004c31f9b0104da15329491060cf5ed530c145eee9891c50d67b8c09a0e2f61cc9922ea05396c602dc677140c69bebfbdd53ed4df6dd400788e820187b7cf5130ccb0830b60aeb8e2852e2e748953451571ba2e96049df7bafdaef6f1472d5a0b615ef2e0b5ba55edc742114d4b0655ef3e20a73d7477d635cd4651e1f4996b31ce076f7b8ea2e286d7dc6e9f6997fbb6b43a019c77df96258771e0910eaa8e82c3b81310224912dffb690f1eab5b1aa56f3e6c00436d5d6477ebd8b3b349d82abb49bb98f51b3b6bafe4b2db797731cd3320676d5feb7e9dc74e091094673d74ee7e9d679fe635a37176d7effd32c9c58bcf69b2987ace335816547010fffdf839b0c5402e35cdde56e56e0b4bcf9f65f6665091bb6c3eac20c67bc3dccca9674e37ba755b2edb7cb2bc95b5d6daed2392b90523121e7bc6a5d52d9aa43ffb80ba45dd96e1651e729fd1cc33b7208b7ae62c449df5c345f169b6f1f6c5243cf655bb58f10e8b8b36304c7a0ddcd07c58c153674dcbb26cfb66b768e6d6ab53b75edd6675a862b139f556dbe6600bd2a5c67426bdcdf2d6936c20110eaa6f5e7df3126479ead537d77cdbb68d3fcbd1af7e48c1ac044f9db74dc3f216a4d917a52ea974e885a9afcef746a7e874efbd9ff5cbf57ae8f37c3d14f27cb5ebecb1e3053ef77e21eb7aec629bdf6df3213812c142d559f2159f3aa84656646ddd29899fc66bf1b67dfc3cdce5eec7d3ddede3ff200d134dbfc5a614b4a6f0410858dfe31960570f09f215c2075ce43111133191cf73d40b643cc7bf96b33c5f7c9f2f06c11554f7be38040b55eff962089d57d6775ce583ab3aaef20176318d67fdbae5f1cb97ce39d32cb187cf731a26b670f1293ac531d1a909cfc7dde7149d98aac9136efbb806c142353ac5a5afd5e352171e1eee8ef93062a169f18b1fa7b8f6c8c8b28fdbc1f3bc396917679c136479d5e99c3e27c8e643235d12219f79ec3810229b8946699f524a29b5d33367975ca439fb74768d8b3867dfb8a82be271768e8b7a9cbde3e1221f67efe1221fceeef970d1cad97d701190a2099c7dc545419c1d0817b19c7d022e12e2ec41b86888137176215cf4e3ec43b8a888b313e1220a9cfd878b8c387b112ef2cfd98d7011e84ce9b55fcbd0adeed22dd95ba49d4987c6a970b237960b8dcda6e6a2e1cc746378b8fa45315cfd22986ec92843b7ea17bbc42dddc2d22da46e49e9d693205cfd36c0e2eae78110ae7e1018c2d52f03225cfd30f8e1ea87a40857bf231470f503ea96040047bf680457bf1fa0edeb18ea570589f59b5741f2be7a15ea48f6ad1118beae99c0af57a04005ef7a69e9a57f5f9f50e4b96b138c74ade45fc4e3e70e0aba58a7ce4b37f2453988c451a48b73e6bc740abed8c610367eba5855f5d28b7cf10c216610e92215d54bfff962182c308674d18d74225fd422481642ba3835e48b554cb086d54529a9972ee48b69804405e92214eb8b4cac9698a08b67cebcf4205f34e2471140baf834c11785f011c4aa8b4e4e2f1dc81779f0d9e14717c3485f7dd106ef8b8f2e36fdf8620c3d30f87491c9c71757e051c1ebe292cf174fe89ebb68424f1795bc6f04dc7357024f77a5e7ab729f3b1258488ec0f385db730785eb42e8be0e68cf9d1316924db84f0356487e20fd7e14c89ebb09685d07dbf7802a24359082d1e7ee59481a31549102584812d1cf02f9dc1d807643da47807eee8266d7ca3e1000f1b9fb58483a0b499fdf1016927e81b8da05cc87adc34be7b7425848ba125ee8bdf4565c1405603b5a4a6de69156192b659581c609d62a66599c737e99cf312fb32ccb6259e6b2a3ced4631247ba7524cbb22c034329434fbd15c70b811ee8480b687e441d051a669dbb09449805876410144612e8c88fcf39e79c73d2966e24b33361ea5ca963b7d8922fcdadb373644d102910c0b3f3c41b95b42f2ac9a0aef322a2eec50984bd40c32c7865e8eb570257d03e3f1270ab630d2ccf40a298c2b74790c8c617dc605112bfdf7843dd2ae1cad0f71012cae6c3a84386a5842b75c2d4b659c43a146b0b6fbe59f4fb61591aa5ac9f379f3cebd4c3a1a19e5d75fb1dc9ba24fd15046a45cf3c047a4b67c781101a723fc95c060a235364aa26f2d531a3b62c3956bca03afb90e8b23ef40a2b781ec0b39c022e9ad1b32fca4b9bc40adaedd781d86a9f9f0758a89d255f1fe0a0f6e63b2c0f3d39df2c3be8c7bfea624f78610cd9a3d252bbfc1b663d2227b16b72260419d4bef11c89023ffd3ad034b0890c6a5fc20b89be89da25bd1d83159a0fe30b198b8b5d8c997429c657f9a45b9dc56c661e7f66953a69c242ed5491296b4bc710109fd6c35a9f5a6d12f033b7ed406ea7acf76e9d122146ad751c08d143b68eb42a1dcaacb80c7467bbee75b7d67a8c69edbaeee3ecd7e7dbe2b98f77a779d4be9884cfc730fb01754b73ef6b7502aa5f19954479ac6e9df1f92637b9dbc5ae8c9a7b1fc3b48eb3b7d334cdb9e3ae8b697e2fe7f36d21d3e33e1e3bed6a3c3e1fc3ec27b978fd1c25069adfdc3a77313437fbe2cbce82d18aebfcb3b360fcc193cf697430f3d757ed82e339bf9916c9f57c2dfbf1f8fd3af7f95070989608239379bc1be7f331ecda8fc7bb53e2ddaacef3014579978dfa05395faf53aad1eb16d43a0b4633def5bb71f605371de7831841bcf5ab71917b8e128388b77e37deb66ddb62c6138ee037b0d5ad2e49bf7f40d73530e4fe7ae8d7efedfa813a67a3b69ccfc730fbc2d4f37b1fc3ee9749c981b25b92691ac88a7d2c549df5d5593f9d0b763eac80daf8f1ecc1e232736c82b9d81e6314628e950e245f9b47183b3f21e3c1e6da067a32a86eae75734b9ed002f1cb9c7ef16ff685d0ad18110d70aeb907d7b7eb13f480730dc442663a0712d1ae8a079a5f104b4b06b56b1460d8900c6aa71d079ae621d1f736e5f6314cfb1856bfcc9ff2702c5f0d0ba15bcdd16f3aed36009f7accfcf2d6350fa5495242ea213ce95687d4a3cbacd22fbefda244a73e3f9b394f9434ae3c86b66ad0d1a9f5ee36d032bf1cd75d04b8a10fe10d2f64383f1423109dfa1329ede21e9e0f36e086f33aa09f07d1a79c01e0a9601869f4b8a3272cd4ce70e6647d67581579edaa94b3911b838bf8a35b6bb36a3d1aa3b2d6ea941e89ce45fc4f2965f5a55ff54ba9b5195dd1326efb8d17b4c642b1d9a31c73516c90eb56c72c0bb5471fbcd0bb4b32dfbbd59188d0e3d0506b43b45332dfeb567b24c2e397beb25cfc348f6034a201df2ebd855ef3d83d9dfaa52e350d0c45f036faac323ff4bea5f49b711c2fe067e7eef3335ef4d0198a4e4d10c7f82aa9c7971d07419e82a108be8252764ba64b5a27903cc7b03d2d67a90c178864fdb07e82c93309ebf1d4ef9c5d4c7a9eac403ca1089e82928b5b9b67ec19a3f4e972fa7439794e660b784211fce480a21b97bdf959f9711105ea0c74697420ef212494d6cf28838c7ef188a854bd36791202ede8966e4925f9f58b4a5fdcd24152039c5f100b19eb1c686590a400c3220784c7b00dc6b09b7df1ef87858cf52c1bec7afdc2a8bd05c30e8ce4fd187663b7c4e7a77b1f2d977599cf2f4cdac0c74fafb55352806b37bb558f7c9b678271a95b720b9729b5cbabb4e3b6afced12e56593fb4b2ba0d32bf3df4a5c72d587e7449fa91be2b6578ec21f7ec2b8fab9f45c36befc0830fe4ebbaf45b3ff9f7bbd241d2e987e4837fc40b5a699501b92ffd4ec0d5cf86e1b5c7a8c4451bfdf88974d4ef956e21754b7efdf78b4b5d546221e999166f7b2cdd927ea55b51082f643a2f1d4bbba2d057e95778ba585db5eb470308b0541a1b1db26d736c43e20c31434811220c88d2f8a1e3470e2214705ebba8f374315abfd6916ec95b359f3eab6b9e7d71ac068692822ec957e9985da45b523dbb9ce42ec71372e033bf5cbbee62273bfaf184493e7c361d5c31f98c51629f517e367e1c6db9845b2263d269bc73e310d6edf49e2a29fdc2ea51c09597446334e3eafef83767bfdbd79241d2efac5b9c6062f32bb9edbb5e33c0b00b9660738619f5ad6462fcee62edb76397c400af79c7a380962c80f4f8e46d9ea43af5edc3323dcb06db6eec38a014e0ce836d826107afd5cb53b72fece0ad536f157adaf5a695d423487d386ed704f29e2e46c6e364e74378fc0c6b8f1e7a7ca61ebdd28f55bf9ffb83db3e22ed494a4a4a4a0cbbb2baec92b01406f8eab3db60ab9ecd4ec9500672dd8ae09065ba918c375d7a1ce34d8f5609f5ea961a75045749338ea008af7a07718c9769105a9f7e69c73373cdb71a3dcb324a35225634989ef903d80a088c7c0646b1d5efb55f785dcaf6d66fd671902961358ef8c20602acc6115fac78eb37f3cd8d64b6647b0b6ea05da2b9ede78e9227b4808def36ab5f7c06a260144f89cb40a1f7f15ba90022ed3664929b5ffccb53761d489af3250379b09e4d9e9f7356ea752b5eca655f0fd6338f0508598d154c8f24bf2e01c336f3d3b3af872433d8ccc1c50da9301964dedd03927e7ad639c9206b24be8251aa5ba920088c3021b17452b7cce1763c69bb3abf7ceff5ee62b7a3d7adf58cc806b68c17e5fa1ce345b11e4ecfb80befe6f2c3a225de8191e93ccb0693be0aafd78e034a812f30e9dc7d20848d1a477c61a3c6114db0c9453263bceb5c94ebf35e376a1e100c3b78eebae6f1b52fccb21510dde2a4e146ff91deaa5b1d7a51cacf72934aaf6ea75d450a560eec92971e81f0e292097ae04ac9d04b50080bb5b3bee08f5c647b4e5ba98773ce59c5e843509f8408f5118a8fcedd03928e84618c1f639c9fe5a6530fed0674ced85dcc8feb96e4b853123dab1e521a6305ad1b49fb714bb208ae3efe2b39cfa7377f2c3da9b190b42c2483f0dad9b6aba987f1c6ee94c4be334e963ca1059e52e73c376a5ba387b51bb461bfe50baa9010ef7ce648d6bbe34789558c9e04231f825a2549e94968c047ffb8720692934d74f2a25b4ee3abf85bbfddf19c4edd6b17bb74297d7631d9ecd4997a55329dc66c25e7ec96509f6edb7aa8addac5811676f0b6a95b2d33c087d91b5e74ee94b0120348f1591a9f4de725b22712773a03855efb8c946795fdb3ad53cffc7297845948f3cde3373febdbc72cf48549dfecb14b326713e1807a9e9ba8d468d7caa3819a4361c7149aa111840105e3154030301c148bc5238aa2c9e20714800f7dc04852389348e4c128865110c518450801c600020021c020c3d40cd50638129bf846b091b59164cdf55bd7816e409633528a668b19ca8cca173a1e6d70d235ad3edef93ded8f34bfed0b94f52d7d919f1f32800e7d010649d6e8aeba1a91673b73cbca5835577c2befa92d16cc1c9cb3f4ff92108b9fdf0555e29725d1c6ddd9dcc20b6e220deb64fb940b0c94dfa7c244d29179e76c77eef8db7926bea2635be423334dd30ff1a807adcf3fd47964d269e8412cd87769523c84f538df36026f58d4ad134bf50ccf3ff0bed97a37cb9d8e03085105db019b0652616eb21fcae09be7dd779e5e339facbc7ba62c3735f71d7aca29671cc192e3923fb2133d2a923aa00b660d7130812006385f9a7dbf43aabc032de29e3cf843a0298353b301c22c7f44b8e51ddb502d7afd73fcb7fa1f62cef7e88deaec6e7b1f1e17190078ed9583fc211044f5c258aa065d82982e88530c5c935325882e50f0d0b960a7c4dde6145494cbab47b108f3d9869e6c6c3cd7a0094063d5f1fa54b5d9d6c87648a93f7dc6db9a84fbf580b55f6b29fe7ebb7bd8df0b5a28d4eb95ad6e2c738be38d3a00751248d6ecad434621071560b250673308999bb1614b116cd1300c8682c962d93e42fbf415bfd774de8722d2d29e846c95c418ee1e188be7522f9ee6ab9c696111d47d8906a763b88faa8c1a145ceb7d861167fc38b20dac9cea12ab9c73c0e08f94a62636272081195c1e12eb0dfe96aab227c601ce3efc0f56be57a6f7dfb3778ea8bd9d33ff72902aeff5da2b8b55f4142d27d3407ac4582a602af4bb9d5180faa474ed46e8db823ea1ea978f737c9ad7cc75a423f28e66cda69345f6a9a74107ac710d9be57a25d1a141cc5078c9cc46282d4d4707884b0c75a16b03de4a8323c224ed358dbe229754e61b20a394f40cd84c8e6e8615e70244ff9bbbc57a1914cfaeaab7c08a1334538c95830b4f5e620d6bcfd9e86be3dfa04bda10de6a8a8958d4ec232779775fb7da656273e985946419c9fb2952ad57069ce9c4cadeb4a31d6e95827669647455d5abf7da1b258d467bcbb4f48444b17e1bfdc0afd71f9292d3ebda7baa3169741de7a741d25f08c1cc3faccfdf8acb484c06d26331a90dccacce505a3a73e2f673e6e536c53e1ee65d269a698919c3a0cd8b7a9a8e3476a827d6f7057dd37bd761b143b3e770661fd7ebed402705eb9e6cdf30bee445a9a44364d4fb58a1d3dea8cfcf4e101ee6e42d3103ffc97ca076c4c57336245f9123e63316c866397715acacc007242095b567187f13c3554002cd7ec838b87e4100e27e12b892225a5ac0d5570b685027b2e361c3056be42a65e398bc79e576e45375e4d837f2205f0ef512f8ea3751d87a1703fad8cf5befb237f2603e1cf50afefa53f8a44bc12904dabac51bd42548ef8d1ef7c2bef98f2a4121ce45d2d00a0665178f3159bd95775e710d8ba08c6191b78a0abd49716277d1a1acdbc4a6332eeab51f5bb581a74302a364e7e511867d1198510a0f4d62491e1e50d9bf42d761b4bceea0867e1ada594b977e20899d8043d1558ffb423631e82b969837982173debaf8bad5ff4bbcffaff9235f0f3571dd840bb0d050566757504263713fd5a504aee79eef417d15ca47c4d2b73b65a656845a99c3e3e713b1d4a8fd097105a0c486b84c00a189fb63e6bc6948b1f2615b34e250bd8a686d94ba2b89b4fd786c84b60da3998ecd5e5243766fa29665e14993eb2c8b7f78d44ea5b551838903110bdbc6ffac364bf4d7a802d97e05dc2885ff538124997c92c9af587e9838fa3005a8c869a47063cd165ce0b5577d6b34f470d9d7f54f9f7df5c3d2f2f862569a0085faea8df8111f324fee4e11f56b47044286fb443bdc7dc2b81ff07e5fe943f71b6dea6a9282e201990ef58102656714dcb0cc96e5f0a4967911d29de6213b5f99107abf5a421b7215bc2ec8ccf74262c06c892c6fd5c73c9fb28f69df15f23e2c6f9ccdcb64666780362013db02a23def7233ac1ee8b441ca258ae4b9401a14b937dcb3aed25360435bc3d5f3ba2f6e9e26dd84af8db064f90161394ceac04ad62f885deb412035169301d133eec46dfa44daa218b4740d2d50ee2839062dee18d20f592b4a42628271867997b49c5a246b812ddcb6da91f8220ff581348cdce9e47e9cfb97b7394cd8ffa3b46f4896b5e89a59074026452975dd170a0eb3b3522f7ec4b58d09e28944ed57f88eef2d2d38851afdf827e073c4da49347249683cd26beef12f50c0a5a416a1645813bf2eb618fb970084f1666cb91c1bfc9ce9c3f4b4cb6ad808f4ef27dd78694897d65a850368dd747a2a994fa961786379b50b31e11885ffbe029a7cb9b75980fc94df2eaf4e6f0c0e906842679e90eb67d16aa9ec8a39790dae8338b495526fc17f320736a265824bf5dc84818b8279015092217e3767c5a6ea00e6a80d965860821df01fd57227cc44e8e83be8b0a988cdc06aecfef8e3e41a58063309b959557c52613927ac43bb94b323542846eb352edaf11478b474ab9c114a4995066f428073d8824018211088bb8cfc6477a1c1c0d7cae3c07336ebebbc8975c2dd879a100265a506d4a45974c2b538052dd1263ab20c56eb2053658a59ebce168ef4ac3b947dd9520636606d6446fd5794885225251ab6c0d31dcbd1e0232fca38d93fd42896c37f75cd9af07df6f3ca46c4659cc4391a37cfba6e3f8fdc65f9f14ada8ee685f4806659d8f28f4589bcc2010e5f208dee9fc322c509c356a6c8d28e09f98fc014fd984a24edf46ad6767a838b3b454a7759c6c90467c5303080dbc24352c55c13bade270265fd603928843766331441f04f6a4d22a993366619c58bdb37035a18cb3f66a1d9146611653ae1f8f1d886718e113e72cc7e1cf38fc231d07963582c5f393856881db3bd9efa3da338571fd3d044de17f798f6b3a573e18fbdce386420946121f7c05367d526027ac53d8c506e1439a5d502c830f93f16d462e7a077416c32c156f422fa0c998cbb0599cad52b3726e7cdcebcf82b99039e155e229c6eeb98de5cb79318f07da7d331ae65458aba2803a09219879751c130ec2dfc8e29ba266e129d3aefff9a3db3f86f95cf03cb63905467a14e08c71a50319fe3a425d0c4f41e59bef20e8cb42d18c1083fc5443e2d994fb3effa284b8f6e6cc87746ff511a3146d94660792037d6e071cf17c4f4d9613c22df5620857f9a74a723132add563aed56c4143ea02f42b923ac7565591c80e518a8df85dbbb0ec7320ac64b2c1c600a86bc9a3f81e671d98e0b8dcc2de22c8eac33a9abf1677acdab4fcf54e22ecddbe95143a0a305405dd0456ef600fc895c94b48befab973018e32bb3948612636c588b899b882134dae693aa783034d2ef3ea8efac58ffba823db0682fdd19d9f2f2e6c0b99dac0a9916a2ef476b8efb70bbed1db0d9e91c8942ce393591b91e9e5e0251472ee0855b201e28fe2368ffd7fecfee3635bd627a710def947dacc9175017d023e19390d8af342b5fc2c3937181fcf8e42608d350396db23da07ee46465300a756fb42587c1cfb1bc2d703a6d52da9a33549db6c55d19f6bd8ee863f0bde5fe099c5a4f0c06aee2cd7900e074b240a135e34c9d2074bfc515be0f791d7ecac837a1e6760c5ce2f02298fb7e353bc1d9ccaace6e05e9be5a00a1a0dc59c3f1fd0f0b90ef15c3fbd29bf8e1928f48ff78f57ad6d17c272f4ef0d20d6edc068e1fe8027d5d6ffbd3064d8f934882f8667b2e806eed07cdf9fee34b8338dd826143fd50a0791ceaf8f15819d99fe91e39ad2a8d9e0bcb9f6e3dab3917c638350329ff99e319736bbce66316f7b840cbef2241b5d6d7748f8e2b6d136c8a6880b5c2f353ec26c3e01a3550d46a6cda1073bb37bfe072a83a99e112e8cd0d36ec9daaf7f60458eaae87f61aff455ce1d3ca5d69be5d6b409eb1fd034ae14194fe6802e938cc5556c6e17d507bdfd0dc582aadcf3691c961968593727cf5c5c8875761e5f395111a25bc9267bfb82a1c71241ec29fe99bac35bf7f4f85ba5c28c001d1b93b8d6d5942a8c1d18daa40ad904eb78dd426d50b5a54bf5e2a961a54ab55936ab90a5713eb85a41082a2be9906642c28c0239bcba1afa289565b217a7eca9533563bdaaff0ec66a84034e2041db0c81462dc71690bf8cee2ac04ecac58ee8371f8d953502b778bf6642e620a592a50e491c16a3f57a692061f67599c4b33109daeae529ee7f8fcd6cdddd3797814f0d980881cb38566ff2ffff37025a1fb6855ea031301ee3a5542f9a11846bbd814c74e22e8c9efedbe5f00c5964a09113181f5190b48ce3b2690d422e45111ae05d7bd81fcdcf86d0bf3c70adf940af4c633fe08a5685b148e14eca41ac58f3f194e8b840b3920efd9823ea1d37f7f2f1f85c34353f6593f04d1088c7372addaa68c5cccb17855d0af8bd027d1405355fe481a23ebbc57efce8404cf74c93464d95b6c21b3c955bb4e8ac864216d199074619005db42e7d14eaeebc3507050d2848bffcf098a6bc2dbcd81fb40f121e28602648898d6f5f1e5ac18dc7798566ac4f0cfeffd734df08b2095108ecf3ca8b77f11c94600b731604639d1616631dca4a52e3894a3f8a1dee6d51781f70b35141c1c82cbd2d0fc869cc10c60f5cd72b1502ca7ee3910b8a0c1617ba71e9d04e7776e61e668dc1814918942326d4587771e541ecc15c38db7e0290a6f89ac0e3b60ad46e22ab4bbe55b1c3653b7b9429ef9ae5015d274582318e0f0412ab125eb32d73de2dc2bf40752a6174cea369f62c87c9bd7c89564d060e8ed19e35cf4c96bd7e8a6705a2bb08330d229a11c8522eca270d89031d17484540a1723ec042afcd6380c1c691a067fc2d8c95bf7f5e09ce6aeffb114b4a3df3f824383fddce6b5244cf09d49bd321d212171b63d29d6948b98d924969789d54a0954b4151075f53edaacb86431494182c55562c713b71af935acc893688558f827af037c015a98f67b219763ac9b003653bc0e10e08157e48821cc1cce08a0c58993421e9211fb52ef8379603b7c4223926a6ca50b871e4eac4053ea0575f5828c31d647d7850b36b6ff2dc16689a6286b90fbc3022da39ef2205cec5a2f87f08527a0036c0de2783e44c80894274b2fe0e29bcf0c2e64c9b388d4c97f37062c2a81c8120288543f5358cbb27551750105e5faf07893d10db6a2e10399d21ae81dd65c85051173e3008508220d7b58491578e3ea214e076a8ec9039d3c098df670460e51a6693853a9f293e811af6178cfeab37cf90bbf5d3f34ed3805af4fd49f0dd82beeadfc56bb0cb1d711cc36aa89e06bea40367c2335f67d9c847f99f332eaa8a3242adcaafe823cdb7a0a87359beda1d1c3ad794cdeb3127d995998fa35cb91d896c172effb70bfb3ff028c29775e948b80cb89e7d5b89ed07e0c4973e769018314a6da209622fbb8411add8945175453e8005b5206a899051af0b644e7b90d4a40d8839b7d2578b29fbe28768eb89eeb83cf64633d714e9f00271f435b38c1095272457f28ca87a217230c3f4f94b8f53afd65ed3f949a5f108477826069a8e1b084f673ecc54e20d828a1e611a08cd594599e20caa5f2f631cb60fc95e122a1b94f2ade624c4a248e8e760ebc1a193d8f36328e24c904508436e7250426cb741f1bbcc2252cfc8ce7d70db8bc237803b48958b0e5c42214eecedf244754ea564e68808d003621b56237ae94d1dd5604d7c682905eb66bdfe006cdb2fe9bb8ebd97c232313c8ddd7c9c3cafea0280873e3b107d3c169fe5f8210d2613c872cac44d4abd595b1ab2ffcc9b23a39d7803758e3700b60be17e3513a33c2836063b6449b30ad3a4f8c971edec482ac63cd90c05dc1163af2f8ffeaac533812018621bf25a30652762c1f009b34b8b8126eb1bd5102b3c59e93a49c98255b11ca355ec13e90dcf9ff0a6b4a36f903daecab70920fc3a56be6d80fa6c6627b4d8263b1f2a31e929d1d64ca5f5afdd8125a0fa3fe77c249f10d9b9275161e748f9845924292e6d26ef7b5461e73a28fe8fd3ada7c163241968c56d1ef7dbdee025fb8fd4ca83c1747ca9c6334826d3f6f9bce906d8f38ea560ae1a7b50c85ff976c9d1450f30838209e82232c4aa24882beec6bb45ee593107a8623c4390e448f5d0542f2b466956daef179d34990c1afeded30eada3bacbba3298f597062b759073496495615ecba65edf06dba7586fb4454045ca59e26cef44030d58e92a0fec712b3d2dc78d8705c459b73e8d791959b91b83536fdc0af1b1b9ce8101d5efd96024fe9cabb58e12f0a01167fc8685d2cba09e92be39961f6083d0d9e5310a5e0947d0e644bee722d3038fa7c52556f2d28ff45796d4d26e86a147f89439cc669bef0dc1172e03dddad8c1e039695be94fe8cc2e71fb95d1dc161c5c7db89cb455043ad14b3b106237b79e11ce2161ca663980b5045e39e15e15e21a779e1054950c6b1a4e90c94b4782a51ec367bf8362586f2e60b125f15d222bf20792cb620e41974286151a3a6b4b94e85d57123ee34d398d57b10e04763ddbdbb4716f5189a2f31528587008fbfac6e3a6ee59d33198eec3975ba8abc71531a807b63d2435c04d0c7c6d1d247ccde10cb926241f4058da9388784c29b42321d2dadeb7a612839f9898c09650c612da3579feeb5fbdbdfcc904a9fbb7bb7a56c45793281bdbef5488fee335ab51a54ab22c7e8b2f4d136e6c0732321919aa87c39b08e7053d8183583e49aaf2aafd9c666fadaadfd12bd670c252034c3b082a04c0f2e0b4159dffc393e6eb56031257e310110e93bf43ba8a81792cd5784fee81ef244fbb72e17eea08300c5b41e87bd9a477b8e34f5a9696203fb9dc8704754b86d29825283884383708f7d70780c7a760f1438d9e5c24d1c7f941625b6b900f784db6eb3d72908d63c7b25bb0735a697318031395e9f29d61d8b37e24d6d20247fc0033731b79542e16c3ca24248182b56e2e4db50c7586b50a84e6efa66bf18ff8bbab20f9ec8ec5f99969b43d7dc8f9efbd83beca633172bba8a8c2a980b46b87a56aeaf354c56f36b413eac1311717fc6d4f26c47829829b6904c832750981df69a40404206e0b57a669c77753485b64a999e3559716cf242a7ff016a57cd3eaf74c56f245581708ab4c2c91a80d3c9344e09679de707e6a362a73b9964d24658dc63195382368f573740c745e072946368156c315ff57f02a07dc3c29ce753f5bce36996f0a7db4244a5ad33eca5cb8da76230cb5496be469f230262002b6ea1a0eefc7083f78a7487806c26cee25818e9759655cfec44d806e99999be41fe55ec26e440d7d1ed393a67c65fde2b3b07490cf5295373eefe64af1d1c84c9560060b05da7cb0511df0a4a5e33cd45f12e929f6abcad7635418b39cf292ebe80c4e616acd4c4c50a872375a5417c4de59a11593e1de1cb94e3aea79b3c08ddc1b0f98c4df4633c96905696d8d74a9ce0308f3520aacb0e717e7dcfd594952a7da4f76bbed58d36aa34f934aa2bdf3a9fff39dbf40dea400d2d6a89df8fe6e8696d03315f3ae4c687ba9e45b92f01b5c36f80dc48bad3f246dbfb574a2d19b76d9f309628aa40538783300789136b459e98e4d5668e661d35027b233cd738f882919b426a8a3879993f9db589603f0259ce5c95e525b57dda322580986daab56da2551130d628621d19724cfa3020015df57ba8da43feab2b08c2416d8b07bd06f38d76c716251470cd69a2238b624e7c5b78c8c3c1f3eaa2650aa92a28eec2369567146585c831af5524d54ca9320ac9531206405c7c6fbf1b74345f767f9e9aa926a77f543048696780ba1aaea45fa94416b8ef781d8ecd9d40e6d8209f3c6000edb524797d03349eaa7786d80172000e1ca00d28aa220c35d310e63400e3903a1d5d53713653ba74ddf49fd054210a0545ed95372e7e7bf22e22701901bda43c29394cc325f8f280185686fb372e6454bd2dfc9340d6b738b98d9c73686d0740154b73040d65da7dd5fbe0b829362d806a5a319931573741951c1c88237616248102d4a02b5c0ae4241aa19cfa7094d149f0f1f0c219088181f2a55eb51cacca18dd813d3307a14d4f2eb43a64141f9551dc311460e9581eb24755bceb307c6044284efed1776068be3a5ecfc4004f9233a4f9ad5ae9bdfcccfd1437e9a077ea3f4dd4de75e16f0bb6e7a10c61df022c8b92bd65a0ac9ac0eefe38c47eb2c0d6f8955bc0cd17d50bbccafd18568b1bea0758e82b4448c5fcac95e43a41b96a0132775f36124b429c2367409646ef634cfc4df1c27697dc853dad9a1b1facb11baaa93da50bc825a5997f6f3616550a7d89ca8c111f99897b90605bebb7ce906dd26a133b360fdc82f2e3d84559d61bd10a741ced851da7e3a7b889f6978cda1e69311e67748af7339ec945aeabc28e51c584d10149bb3e84e06d587ddd1220a178b57cbb26717dbf3a6856222deb9d6b9dbfb5a808ebf23445b4b11a29a83d828a405396b599d4db96053415962b7be1efcef63cf132c87dd2d0f24ca4cb01709117be352ab8c6f0ee1ab61ba0fb6f99567bfae001b7cf41f563010239c794047cf426510dd1ae87875a0a10912eab18c4fd23ababb9da6543085b36b14d1640341cbf269c7b4dfd93590e09a9c81f779dbdc161a8d6ad9cfc629de584f0d8ac371be9bf899bfb2548427c8a2fa8a3fa2b1b655b688ed4ad38e193603ff48fc2beb37e9d39916411709c2da206f2e85ce2743b546ffa9339656ac065fa0fd266759cde577cd564923b5fd9a8533c7819c234e31b4ae0bd6ab3cf0fd63f48beda23efb0ece41eebbfec1ec39509235b625619643402b1c58420090d3642274bec21a86429bb74e066530e395488242f42bc882e880dd400ab3fab395ab4942df3b0eee1b79ea5af5ee8435f9198865ad2828895744929c225854e1221edaae0c96a72648d51791f89af6080262c6c4e83830f813142c00080e087aa67910764a2f470a4a4007e7562acaf50ecc283e34ad751b13b3939f0ca41b30491a1140f91d46c631ec7a161e5ad27b0579d24e7f30157885aab8ca6a354331e43e13759956c6f08d296cc348217ad584e770aa4a55219d2b914675542eb966a1c67893795a7745ba33673f74d3aba39b27c94ffab774a98068cb08a096b54546785b8046d4d0941d4a7ca9aa6273132186cf5a7e0d157b0ff98a26f5c27ba051934bd48c4dcc56360704693b2ff9f2c0bfc536d90a25697ce4ad5e19bddfff6bf4cf8e0bb40bbf5df58d27c2ce5ff3536707270a234828bbe48440445246936c3b85f07d3559b9f19014f1bed3427352c5a1be073262bbc22534922e44509ba0c06bb63a792f0145eb7fceb68b09698c8276d074e2dcc50b9ca49700a6a7b6a78135c947db160a9946fb93a97830d23cc1a67fc8a75da26fe45d26b266ed98066cc454dcab90c07c74cf336022fcb0672475974ae54ba345c8a44800d1dbd3aacfc2a93e4aac3420e15527b0a3919789194641072555192d85011b91562967a617d928408cb83d03f2b5905ab26792b423a4241ee09487906ce527d5d2d72b1560801a81c4acc02f56d63145c8136e9dcd55798785e53590f809ac49e50f7ff0e17500398a5d6347f96c119ad816c6f377205c2a1a4ffe108bfd6f73a8a2524c943a9420c1bd10f1afbeb0247a7a61415ca80a6cc413acd89223065c080ac5b53f1389cdf895d9aa38b0cf62cb39f5d0b4b5ee2d5cdf09c4253b35cebb202e04c8d5f328c32cb67b38f5a81daa33a95ac2f0bcff768305c4761957a08355f308c53ba8136c2e09902ca79c968716b09b06b2054aadad4069015c4a4e2a81859a1124618a8b21f9f3ad3ecf1bc8f00d9cd422e57e46c1725076b7653ecba7fee5b0c40b80c310115de0d4f5479a943de9ce2fd89fcaafe21dee5f3fbe26e3b3053371484c78aeb1392a8a7a8a0f06977cc79c533121fedfb2e593fbcd46dffb626e7bd2ed2ab66501e825bd75dd1a666f8f60248e751db2f9648dbf66e06e8367ad37b4fe3cb9c09f5b6378f5a1e8a0e8f25d11aa43b2a2e7a0a980b2813eff8fbc922fc83270a1e32d45a09ea051cc54f4ce2cd780295cdbb27c4c86a54f229a821e3a904cd7ca9a287aec5994a04833a2738b3a7f126e131db70661c08197386da99213e749727445b71b737623ac01ca10f7a96e350956afafc9366f36f314247b438568e4b6aa3a2ae5ffe1ea0bff6c3aed53e8f70f49c6b89862b58dcf4c7f804ffe551e50b71b0a8778389ef034c83886a15e3b0ea9643b2e3cd80b778f07e6087ab740854b44ced8367c9ad3b8f0617f8272477b8cea61423a325878230643bb49b870f1d7675e8e64110f6de79b0c2a5386241f2ceef4b6f783c2e58e511a6a1454f4b2fd375d7d20d4f9cf9b012c5d0c4b8dd2721ad6d883db5a9eff0d91257963c757c8b8ae75c044ba5988bc7d1ea2b989bcf56feeeafed72ab06acc0cb1c6c39a17d9bb33ed06a8da6b5a401710bd7fc23c9ef5c142e7c1c26fcf71f4f2cba0c942fcda52243be7cc0009ce8dfcfce507925dff1e0c385653643def049d2e41486eef4a4e7927c82a648f0f832ff1c44f8bf198865600739f4a74907bd9ee7f746919eea8ce808547b55913cf420bacaeea4840ee31e4477ce70ec172c933fa796623cc224fc0333f0eca48c2e088a502ed404144880320ac8c5fc9406bb5b498f0e03fd89af9d457d6b60d66f388f8ef13043cc0be02fa851a647c803dc813e056def60279a672150d68e8e5b2e089d77570f6da56962dbe65007d891bd1314abca011ccf411058007c40b4369dc6d204c2a3d9f0e33bf6c649c995789fa67ff4eee9640740daed9ec86f64bb67cc8d9d3d320c53193f8e525d0cccba572dea90484260ae322006fdfbfdec9387b49f9206fe6f4bc599e70fb576e34241b1b0b3cbd66c561edf4bce1ced0315fc86095f979075e0400695a0051f581585507f316d690a9b2ce605ea9e1719ff999b8f10dcf5ce9419fe087f6940de04822fb1bce78c029ec6c362fb76e16ee8fd79843e0607928b2dd18f81f5554e4ca374f5838d74420775657aa9b1afc8a0a43eeb93d32f23709222fe95da5f0c7c6ce16c9bde5171baf567b7c162e0b4606c251f76b50f1859da0748be0f2915c38fa0269a0e9d497fc49bf509047749416c3b1eb83df939112048782fc10fba651b5c2faf4103cd135e82271ca5ad26e1556570a56e4a57c6dde0416d7c6e4ac498fde12939e147c4fb6c3266d3dfd64bcbda8f5fd25c864d26842ebcfa02397763e58bebd76d1ef4ac31648f2ac08ee626f35cd2d904368e161678e808ceb5e491c6c65b0087f2dc33e3362d0672c3d2477347df3beb96749853435a46116351611f936d0b323be547048aa45acddba48750aad3ef3e85f5904ef96839983103f82810fc6a53c74e606af37a31a185e18704cfc9e34c8039b5870b519e0b57b31778001790aaa8a5ba19aabac240ba9b1130ffe8e23b44b9e0d5036d6530a75b5b67e69812553755d121ef90044fe941966e8d6ed2a3940b70952b6054274f5094d04036684a1a5ba4fefa08d34c53826dc79f186a15ccf9dbe2b4da6a8d7d1c7643662da0ff480d47a128f1c56f44f69be84a73455167d2ecfeb4250be4a3a411c80142ff38105e72acf1b6c374f4a0b0737e8457f5cd6c6efd617f5593756b6588a43d8079b24021e79f4dad6af51157dad170a2d1cb431b1f18a18147d460317e3caae9800a69340cecc7a99c07eb5118d1484ca4b9be0dadf4cf938ef92708bc2cc2acec41c497211177be427b4fdf54e838db80cb9a890d5b24f191bc6f43406263038d9abdf32486cc81974de297b21b121768f14e41c60bc31d0f75fb50f4cb724b50044d4392684a7a244a3bf41805c9df3b5a63b1297d9caa240a0ed762e66055b6bc6dc0e651aceddf567abf4c9167b2b888d161a3035e401efbd6fb22155fa432989938bfbd1c68687de810d8b57b3a701fd36bcb133b21641b3a5ea372523c0b7314368cfb71040903e26f8017e1a3c20283abf579790b776d56b05e543c1894ad48286edc8dff795857d9254283c27e465075873aa27a6f454d3b130eb53987a090a5ab188e91a759f85a9b7c821451a2c6f043c9d27d6a3ec76f743a5efbe9dbf0d7022556a2726d6918edda93d2e8274cd9141256b6eb8e291f29fe569b52206544a4524111b43cc1ef7cefe8dfe9862bb24ff39b635a754681613d266c3b218edb9ba5f8d74ef3b29ddabdd85050f0cf97cd559a1468f1aa87a988b7d2e08dd66f357169d372087c0eb0a6b86199b0c55287970640ce127c43e380137d6d72738165d5998221447ec803792f3e3e32a4f6e80a068d0bbfbf87a735a379c36f9dfc4d8af4cd24d0d1fa364364c90acb3f64fe25662f7926dce9005b1fb33705835ad9cf5db5b271704989e20ef0ff85bc61cde16c3a67948b1fd651c6e38e3fe2c397630d96a1a972f4af4d54e8df867ec132165af6d1a3bccf19e5aa8d68c47a33b0d051016839502b2256882383785b84ffc498d66e9c006933104b08c3ad32a3f6b0ca05db172787f89cdac930cd923e14263368b4c2e29e91bcc96476b178e833a89aa60aa1c2d49a83c8c3a64d7545b48405808229271248fa95c645f47768fb8ed5cbcb33c141b5b7ed93e54f8fad867319006f8c36965b201fc1d0a7ac75fa1cab979ebff5e4c1c213f8c60d52ad1d39d55377182e24b34fb4a01a3d072497abadea73eaac7ec3a80c1e034bc8c292c57cd3749e3c790250603d144237337c33abd41df15068c64886fe9832dbd6fcb004baeb9ce70e4767f670375a2ea6411333a8661b8363caa74a0a742d29cdccf742102ae0b16e481877a6183ad2d429e911355c517a27ab9aa2f27ea1ff955e63454ab01a186b76804211392737307f144544522e829a28aaed0df80c0614d22f93b4258502d121bef27c1df7f5f26184703a738c7c44d2bca336cf230608961dc025267b5f4b4a3d121cc1dc9ee8e542b182cd1165c237b14f61f5b3cb39f66879fd482e5037c9a4191f30458b5f6235b8fe29eca7f9488f5b31f27dfdb2e9e1b6e8a62b97561c051056b703b7c2ccbe60402f99c74aa30838c5d0e6253a5744f97b45555f7c10bff86e52891c038403378ed98fd1fbb3c599fd7ad997373a2fd0fc86fe3001314c88b9233abfca23103d023a151c9465ba199a65ef1e18f601844205349076f12fd70180590092fb13d98bd0ca948fd8fe151e1ef52ac0990c8f17fcd8e000fe0dba7d81d533d87e8f3442b757ccd038302148cf483f030d0cf8aad4c6b8f3e0ecc8826b219ef7a56ed9401401cfb2f6b52edc0e615f9a3475db59b24ef1380b2aea6a21348515ebce65abdf807677f1f2cbc0ac239f99e5296a9284d0ab55ada680064800845924ecdad8a9f2171b7ae2d866ef1bc523dc9b6e8609abc8e16381f4968302a7365d19ab60cbb3009ccaeeaa0dd815f50c197133d3a06d35b45601a6e022a563b2206701969ddf6f2d605b9ea40e0c363b23c86c8d1529b7a4d7e807390ea78709b243a495965058763113ebc493bc27a3ffa4717de9c27173f4226119d3f17c31c8387f1922cb911a0d171336efd6d38bdbda3a77347f2bbc407c2ad9a679b64fd5f23c8088694069ea204b6d35c29e529df7b0a0f8e562241a2459014b50e4673dc72a096a8b9cbd4df8fcead0c84197dfc22df2cc6f1442646162d75c6e1ebc907be3edf12c9214d9f7f7bbfac1598b2829b4634a8456415a94f2becb1965e83fa83b885776809a0dca6cc7abded0cf018a943b5a1e4408fc6ca197025d673ddd28e25f8053561af713b4cc76d9c6ad58008bad70f3ce3aade67d6dff0f21ac95ae5b0d2b710f6194337e51a1c63c19a19263b6900512510c46f9d77fe5dbd8024ad65a4601c00a068f0ddd4d91030923a93748308d3302caa2677d4d0e7e26d3d518a0e989b9976628146f43e1f0b56ee1aee745a950aced30316e119c7f64af6336b1eb1b16cc17d34c3a310d65989ee009bfbef2a30c1d2f2f77847dae51cc13f129606bb6631476183143305e902c143f9ed89dd7eef7472e56e894c69d2952e2157a5ae18e909186dd91b2eea7aeaf24117df1187db6682f0a054ed409873cc35a494b675546fb1930e3b91164eddd560baee060147d048339f4301fa3abace0fea5e6839573c513f16f9364bde4c425f349bd486a4ac0f7c874b5664733527c0f82b7f69321932886ac046e5a99fab0c8bd5a2997a90e00050494a967eec2470b668a99027690aa2fe4e139a6c1f6b44fd51a6d0608fb831717857d125bc5ff918f906d0e3243f6c17d48f48f667778dfa08c3089bb87004c4657ff6def5b647594141cf66576a97c65967b86925079f2a1dad2112e8514be092ae6c452420b707ad65c05d7a1217d6d0009292d5712bca216023ca30ee9ed2fd358f148c1d9aab444c261a93d7d0d70f664f62bf4e09c4a14e5cde8951cd83c82214ff26bad693cdae51b45bc3f6694163e0817ea867e005937db5a2cac5887a83cfbd4f443da2d829f5e13df353d121dded38983d0088d75e9b7eb45645e6a50f6222f91f6a4f685911790698f581529f4d066948f5acb302162c6a01d41a9d11cbe0b37f0e1d746c6318e03df7c09cef29a48567e4102e9a0b3457126816d466214a16a5abfc355c9bca5066dd9b4f1bccdfaca2088cfdd56d6c01bee455170f13170e1c2d31209e6b6c6aa72c22a6de594b51ce3df1e1aaf2962ea2d75089ba5817f2a8eb874dee115c83317a7e13d57ea98b0e3a91ec0fb937d93ac4a3a36aa0a39b0a90ffb5095d81904b8f969e49253098eec130c9450b24d26c6b00d62bfbf7c6f8c4bf63d65e83ad066d32accc21e5bcddd6e5b9d0d9500ea3f510972e5856824702e7ddc150e310b63e93b617e517d240139bb68e1736f71f4fca4557ef88b32b23b84dc299b43b1f7823fc5d85687f3847d90ff5a0bb48db1fe461324e48c5b8d95693533757b25b7fec1720bcdd5fc89dd99c9e92de8fe4662adaa90eead5385abf99366808857565f10b05132aa3b094c0cce9c17a326d31be0305c3cb690de4c2a0a84b6f66568b0cbe8e04f95851456758b345ea45cdbf66440108154781063ef85b335231d71fbdb7bb25d64fabd985c577b6f8cfc586761f748c042c0ebde70b27e3aa423a8931c137207b21015ec00c9a3650008cdbd6445bc254448d95e12af63f6eb08894568bd11d01c8319104627d9944dcca06532437bc1d430a179e3797cc60774fbec55d63119956b6c69941a0a3e511877ca8264a209bd07af41e2e6fcfb734e0200130249932d7fbce54d8feb3b4a36a3a7155e64ced021b25c5d6385444d6bdd26d8fe6471c49f14e0a24b5a07bb2d63b4bfca68d0805fb60a8c63ea6746a1bc94691b51ba5a36410467d56e082c8891cb0f1d161c8da5ee3f398832648f7260dc14d486ea17216b90af5666c5117e98b36e32e40e4332ad5a06167e01359d1aabc4f926840ff6266d8b11d12a2809bfe643e004263f4faf73f52bb53a1b7094fe51ced18002e568fe0668bc960c0a9adb373564e6687b4fcbd6228863140e7a8672b6a6e57c0a284125f851d6cf2b0d86b5a9525433e85155940d0e6d3339d5ca8090cc4f385d1dd90f3902ab9e57c0f7a374681f61700b8b5e70e09c41d11a6367c71196de9988a0f8ab8d6542e6686c1e4211ea28d4dc2479b4fa39b0315f42f78647824ed9845ca9c3c2277770e97bf295499387584981ddd9dc29073a34a6b3d66f1093e52e7ec496b4e9e10eea14835df2723a71a2b4cade8a8ba5a3dc7e4c8e9d7f6d624b3a8ab2de7513b94ea882cd8a9d00a1d64f7ba6ee11e5b9a58f8a6c7b6b0af7bedaa2f700445dbd1fb5471943a6a98e638f18d8ce193ffec7d7e725a45e5d435b90f8da053fbd8ba640cd90556597d7d55af464aa15cd94dfc8b9baf8f634db942d80be9fedb48d9ec2ce05c573401e1221f4b5dcb3a2a4fe59f54fc8e74ca3c09dc29426d0a0fe8167719499f632a449d8e3283d71aecc5ff31ca8003813927bff6ac1dbe6811fdf4a467e231dd4ba385943f10a1d5855922f89212fd54f0e87af71e5651fe720cc2c2ac2993fb177de209f31defb63b77e31c3a9e7cf106579e04acebfa2dc595710d6d3cb958e37dc4ae5d62f7604aaab1c0a090aa4bda004115641c05dbb5b2b40db6745c7ea9e9809a1027e23e32bf67e10e520dadadb26629dfdac7a4331b395c44bb95cea3e7f40c076dd0de47104ff6c8c6440dd28b49eb730b7677f7f93045635daec75dc5791fffc410490e3571983325384c37e73d4d24359dbd1052d22b35d615bdd8a55db1c31f535de5d210566992df6518f322d0bf4fc340e5e570ad6f6240c14321c21d83760191e067699a412f7df4096458eef87fd6bc57eb12872c1dae93db28823fe46c0a88d80faf2cfd3a09d400ff785b9839aa21d8534cc9852e9c4e1f83800c4103d24e5bc0eb0140480c0bfe451731e23529fc5f8ba14e62f0ef72bf89a54c531c9839afb7cb3044a7ac30cbe2ce6c1b043ddf5336d3806da44dc946a91bf9774e4683676b8ffbc705612c657c4d0945ca8b8735b30ff5332f39c535ee8d4a17bf72564a2249ba81b73fbfc8d2be22e859604b79b9665312bad73ba6a03e9091907e406f7dd7798076b75995d488026dfad781c06e712820643605c09164edef84e10eef6b4bd51e8f24cdefccf09533313600e75dd177517d274c899d71eb89d78d941738adbb17d486b39434cada8c10d3e62f3338960fd3824476a7d442a29f6f551d5bfe24be815471eaba870b35f7c57cc5deb665e08c18e573ed8a629dc3779ff9acab573de96ca178a7fbc442381bd003f46e8a4602f699e59b02a4fce5ee87d56d4be771242ffdf7ef96aa517fdeb69eb3db2a0ba2403d5722c704fc2181fc85d52029fd343b2373c61aaa3873921c021cb7ed2f112ecae9610689558a9ab468b13c8004f9d1f2494017ffdca80abf1225f3532ce022777fa55864342ee16e9b12472fa29563724ef6a989ae99112b8d7b95aaa5dbfc8d28de487e0c1b6dfefbc476b6949d6bb0193557ed820b13a681fbe4afcd441da6f61d971c973eb6ced55b81145dea4fa63e7e22605f5ce9ca6c2d4b6b1bab005b031d7ab406096009f583987fb7fc6b5a7f6d6df8985fc3469a6db47f9ae0b26f9395af960b06619adb46b9f4001d601524e5b7210a0037de9a45cf08fcd958bdbab2557b54e0e1a0ab8322cdaa3a93856d0814b16597c244be100965941880977000f456499e431648e217b9ebda9c079844ec31a88d2d7dde64da05ecd58fb90f1cd49204e91870faa9939cb84ef09cb23e51ba3a15c61a80b24284b4220191d37a3d3ae4be9895aa6f5d3aaaab6e9a8e593b56c070b6174ec606e5779562d306b705b2435ec194c1a68960c1b34345459978b5443606881a66a186bc12cc8b69837922ae16aee4126811d3f587d86f4b8c787bca73a352a7539e151f2a3fd23d6d19af3786c8b643b567db574cecc0018597746c2b2c173901569402c43ef629363f7332b90ca63c0a6d5b326ee206a8ec9720e039ba9f1ffbb4a8674f56b3062191cc2d52d7ff189f8d329e428fd5178d324ea559545fe77272c414a6b61f880b38d90b1f6d421d066c3643f3e5d9617920f9a910a797ef912fc82ce44172261fa6c7732ac6af59871e431178c9919ad7d6030a12ea3118085a2a329a6bff803bf5b2a63fe33649d3b45c36c1bcf88c839f053480c7eb7c2ee4b095b1cd0d0fd0faea3075eab2d9d70684f11c65ca85a79058a76c5358de6fa62f788ba02926a0fb44b5901c0f71fb583579f39593c0a91e3214d3d2e30318f87fc84cac3a10537fd3ba17fac68f2457bf7bbdbd60a6ae89a2a8c53d76b49bebf2ee51ae103417b06ef0ca1f8134db3df3025c55fe0a0af4a7231743a41fdfcd51ba45be78566bc9f53b557f87c0a199e84e7d7fe0f733d8f2445ee51c95882f69c331d6444bd0afa4d0ee5201aff7d2a0e333f30b8647141c90e9181d54d2959bdf92002dea6113f9532abb7d20d2d4f53dd3904c4747cb9a908d878cace3af653cc70da68d6f0a7231fb1436c90166f66904445604c51bc6fdeab92b5131cd951ddba4d0cdb510411f0dd91bf23aabb516cbb582ff42cf07fe523e570acdfff3c045a77951469c4ffdfe0f5ededf0e0961315b5df8871b90c794e5b031b70aae8f3c6447a496e96cb41da83ad3602a3701214caf4ccbb0d938b4264fa014bd52314093d2636f425f31fc56f83b12fbe8e990422d8301a6b57f4e4d64400eeb4b62619348ffb08795567774efd866ee099838b87e80979c3379bb045f6b5f9d3b8a1ee3bcf6a73716971d374a2a142ed0b4c5a7b8b4adbce8cad52d7250f0e88e198cfbaf3fb7cd39560ee9447e7b54871107737c52a9ea0efdc6d93685a8d86fde9f30ec404560502d0afb483de994b0397fcfdf55a04231b3dbee5a71b8fcee6916e56d70c32b43a10eedd2d3cea8f300951c6dda4dd82f3a58aba5e3785a9011ea1af25bf8e137bf3099820651b5a53572bf1789fea9439b0482d96f53b9d2504cbd36257a7d6fe6ab8e16b7cba020c1dca6686b421689d0688d85e9f0f75df6a03789539a17d9c4855756fa1cba40cfc112f410e8e6d495b1ed88aa79284c2e567608940f1aa15ca8420065a114e576fe7c3b34a8733c2e9b8d7ab0cda5ca731fb5bd1de525e3029179b86ca8796b39b285e48e713965d3e24fc57c6c0380c42c3ac406d669ad0b8948a82ddcad726b11786069c23eb5c5a6f16d57c6e0f5e361481826c0e9b67338b555292cee06c3d0ee4746aa4a56835a0b643d06b9194d7eadc6161f233e8cffc06a8131ea7f02124e8dca2652061cce51a0c06e72889cce6880cd9c33569f3429d902f91295b0a9960aebdd0fcbf2a342e329f7832fe904184922e86eab118f725f44cc4d55b679d656b60d9fd814727fcc06feb56d031c331a6840ff75f769c1f0126962062f099aba6bd506ff58e5a5c31a0bde5811c049b1ecf70312ba56177193020fdf4ae4c0b038be0c7f4b22f8bdbbdcab1f963b7e719e0ebfe77e94775ff753abcf38471d6bdde210b56cf3a33c8b237f40a3aedb316aa699cf81f9ec23d3e91a8d79e4540212db720d2b89c492152e790dcbce0a6e49f46133a514706eaa325b0f223e035ded4baeb08fa20066ac6a9aa5c44a0c35743257421720b72becd8873b489ef8bc3509673b17543a30ab8ed8930d7ed80d7f577161ce4db8a6ae5ec5fc380f45a862ec9ba7ec114c5e560c66571e127502211c0a6893ae3313936bc1eb6be52f88170bc9b1d1f937982b16ba9e5e3f36b8a436b722786d156b9f6d8e0a4958c3e1c71126d65c04f0170a8a7f19a04810c1e75c7403fb9a05aae0e1bd08f0ed4009eb4a78ae80be67bff3f3498c3c1d312b7ab339f08c0f76169e3315656d041e661cd98209ca50b8bcd16421ec4d46e7f03c476cd2a757b751987bf07f71f154ee1a336eb1e853c6422e26095a3665226343f59996bafb2d01a4f96c95dd164a14116c14cd626a92c3e8e200dda7137263675585ea74e7126d95f25e90e07141d3b495395f4f29b503f12c4c1ea53a7bd53cad8a80a9cae831f8fa59b06f1f03c3856aa93343b51dd7207d5aeb00cb8254d5cf95b408195694188c1b4edd5aeac49b10d62dac787039d1626b6153642535287aee831c480a4b889e790557b9a113b28b3725c0aef6467bd34e3ebb3614f1de133db519a0c0872a32f48c3519f5d7129a52bd958848b84a4ead9073017ef94e2a1e175df39a741fabcd036339c0891c6c4f4b0fe521136efc8a92a2d2cf5f6d4ea91580158c5304cf76d8dc82587635426500580a78214c44d43c1591c5fc3032d3539ec6b05ac4396e9a3fe8bf0db1c23f35207c628cfd83e0c4c7afa4b984c16073abfcaaf2580d00272b295aa4b11ad15c8c5223de0a2576a58b0e19625fa0a0f3781bcc9dcb2820899df8413aebd8bc801c2bdfb31b0415cb35f36ea2595ceccecdd3d0191a56e1b305e49936372c9ff11900742dfbe503cfefa0daca92a6d05cb8ff36cf94231c22fb7883b8c6e1c666d70fc63dd3d219889bf8290a02720b82aa7352c4068d39a9f87d167bca2f4b6448eb809688dc994194afcdce2af4020a2eeb9d7a3317b9da5db2b784adec2433ab43fcf92a5129914b07d9e70b0e5a89d0805e055f44eaf837121e6a9aef28a7f76c34d83e4116cc9e22a688329287d89018a46f5e7a60d7d6d31de652a4ec3e565ff338af7ae51b54db9dc6ccb769549c593518727f2f8f4b73d6bf9db0c6ef49793656ff9b441440a94ae2fdd0084c0a72fa524c2608d887f02c610ac9430cb0c2a1c5c1f551b0ff3dc47394210e3e8bf24c2b69c4e733d2a01e66253a6e7d164eb717866cd86d1492c0a4efb18ca64a4c0613f7e3818a283108137d01fa8613da7ccd8917b57806793276387362f7c87156e8174f05062e6abc042aa9167b7bdb44706aefa0cb43e76ad47bffc2217108cecab2b4a96f4373236526dc1606143454133d537e48e489b904cbc4e2623519eef7a258547f847ae6b39ff0126a5cf447483dda9b441e245c5d4a773dd3ca782ad51ac3933fa7e27f1479af8583e2c1480aadc5a5aae7f752bbb115e0de2a4da3c8b7cff43d47e6a0fff125efe1ca40dd9d61b1775eabca43e0de244cf30665fa040d8af73ff061851b33d883cbfdc23f6d07700719fd3fcd4d37a8b3ed9dcd6b11ce420e3eebce96a99b7d97dc005ee62f3f0d17b233d6cfb557b809799c95e6c269e655e6bd6c7e6207195bdb5a2faa8f3f533bfed4206763f94760392708a483a3c556b8b498537f1dfb8e1695df90c52accce2aeffc7ee7ebc55f6594b79329770b342d38e627ae6ca5c34b9265d0d8dfeb9a97cf9e3993e242d1f3f2cfedc5e54567fa5a3757f7f5b4c4e8c42d966a7920f4f24cacd2946365c561aed74ab7d61eed451f66c22636ee0022c34061829b51364d5be70977cb83d9c676dc521982b108c3eb00459c277147fa924d0f31984c46052279a8e226be05e580b8482efb78dba4cbf44224d2d040f3a9af8778ba7a0bdba4690353003ed014d8715774c4b39ede306b3091b75bc69f4ad4be7945b09614452bb13f7273164a5c6ea5fa1e951af14ac338aa8d3e24a038a442a9ff24b04e7fd77c179ac70229619694e447567afbc51c6adb8b8701c31cf9b8fed58090642e82ecbb2cfa2d614e5755d921efcbb5c989cea924eb4235195541f83861a51369eca700e5a19fca6f4193f8b5d974d3b0a2193528101238dbf6b9c53c9be60379c4e2c6d6287928cb449da7f79e9ff1c7f30bd92901e310b36130a075c968b9a08f27014ddd2b19bcf94b3b2e9b79770a865ce822044b2d2e1670c05f8de2ab1d8b59bbc8cb04631d1e0f7d9680429a110ecb3e98c53cde65b5f4a4806122e2103a3c9e048bb9c86405c35fa7856907a16f9b2849c92a57fe0ea45a488ab2b358b128cc070c58f31a07d8c8c8d17c05982be10f7795a744622864e88dbe0a5ed0f9e07f30325701b49c2aba271bdbc592db2df4188073fa47e55302ea1a07dc6c687cfb990998dd21b9fd28f1382c979599daa914f0811ffa3fd2df7e0ba0cf491b19c5e684d6e6d9a8b37e5ddf872391d62330ac61a80c14a3c3a23866ce607875ed3651bcf89e78f9cd8582abac1fad93aea765c7e9c0cd217ea3e90f66770f4562382affc382d82afb5db26ba829161ef0405b110f5a075d5f794c5930a05492350c290ab1200d83c221fa87b5c1a9f44ea416219b3feac686fd84a61f0d0002bee609d7d238b157a0e1c07592066841baac41a59e956d6d525130d2b9ddfe74105451b0ffffb3fb2d9ee8f98013392021d5d565602c53267f5d9521b8c69ea376d17edac204a10b6cfab70d714ed00e98ffa51504ea1759f62e518ebd47dd60038926f360b0500fb248cae66da5c3e259ba1da7c02f4a40c343d44fd0fc5d0b78bbca72abedcd0615beb0d4972333221aa6f9becdc676b1cb167ff694e60163a6034bc145f57e0d5f53fd58924962567f21a8c19e009fec5cfa07f8804b10978f1c20f063acc86435dc8fc15b5d8e13838e8bea1cf0ef6f57c2f398a95324bc87fa66591b63818807bc24a04ff7655687947806bdcba53d7739c735425ff441495c15503078db471d59bd147666790024856c4c3aef67a3fb412b2760fbdb0680ddd30e84a98de68724eb40d2820b69fa3d08e4f314280d6b426adb5ccd0d6c8fa159290c282df19abab1dae88a929c40e9aa6259d2770a312b5c3ca6f1186a4fc0784aae13512fad1828591665ea6d362aa444485a15f2666512ee6b2c9e7aaca77ba235b4c43f80a04ab9f44f686ac18b3fed9e1bd0bc46e029427438240d30854beb760096571023301aeb0735a7660c59fb901470d1ac1d57d19a095016f9b1ef4d4621fa0df80670514684f1577130db22b416dfa364efd27f65531870482ba3bf5ef2e362e7ba9f6df4e06cc60768e7f085236b01899e53bf7a941ff263a300fb5c6068ec994422bd019969f47a3abe065a9cb3aa3402b7e2e090c3578acd7f500f13c82b8aa3e2087f46a777ff00b0bb0b78af9942aecb174d4c71f30c088a838307a9a75d004b8b7312ef6f39a7b5060d5079ba5b626e6161ca8fa73596d4bb52c04d2640604064654f82ca2b2e956098a4c563b9cb5387ac0d9f58c060bd580c8aab7a22492e12160427dbc26208bd9954b33a85d27d344352ff8a7c118e813e00c24b2400a81e3d9c7d12eece5fb42b25dfb30d0a0249f673affc4b7d0e01e657256cbb4ec09245c732fc181210e3290db619b371d529a7efdb52de4a36c4d3ec156c5495217e3a95a807d62874d70aece49835c56f7ec186e4f6e2dfb3a9aa7aea871e8e3848be0038df4aeda973dc7cfb66e86ead13930e5d144b1aff666cbf5d109cb8e67a1a392286872ac7d57f7bee8647ef0116698a50d5b8180451f4a8ec46ed2e811f4983320fc570410cc080198ce51c23eb6cb8ce6e7d1e84671d0eaa36b73efa25024498004b6fb6ae8d584106024547ba6d01154d2b9327c2936815ce4638972be41dce0d2a211a97f2843b18159e5328f751dc2ce86daa8a2d2952e6ae2edd299b8c044abbf9169f7e1f5dc36d6e514eb03fb1b0284ca082190b54d425f23944bbfa01574918151472471e35ec17285a2856cb83ac55494860f916b83d2382afa006e3e0157cc26027516e5378e11c1e4dfd5e8c3b4d3ded5b0556e9d5dbe82c2f802cc55183c0392fbe63f45db5dafa6ee6a7ddf1b1b7104648d26a7c61c78913bc87d6b08ece3b685371080dae71ee6538d4c191ae8fd3f9c1cf5a1f3e26d5d1d77d8a517c071a69a0ae1408b03373397aeccffe0809fa9c939d52aff26146b1120889a762542958eed022ff963f96d10b60ecdf3673f8813cbc98a06664e6a8dfb1328158b66c20d457af033442777aa2dbaee7776c575f40117532b134c10627d7a6fdc35950406f820ca8136b4bd77103fa1a7360b33174307154b19422f1572dc6d25e30024d6516576f5bedf603da07cc024fdc9a1ab84cc3f04a95600eaa8bb42a2b8a80803b5b07b1c072246ae31d73aa89758a37227279c66ec1d8c48a560e2821caa5b96deeef4a43a8073e3a6aa2b22a066cb7b9383962338e50ce83786b8c5db418b249dd36594a5d12157443b5c0a9d94e2cf7e8f6581f09b50842b77b291116ffb30ace383750e837290441f4e3c22e31561bf6d7df02b89566efb3390b03deadc619fd17baafb025ab46c0dbdaa489fdb299f5c18c189565358950822d8bc751c3fa6543ec9a1e46b05106a315f3fe060dfcf16898fdb27e8c8c6a9800bc4e25991adcf46d432787655c5fa1be16fef5f740671aea18537c860905558c390f30a32d06badc60791b325512c78e8a4d46478111c93792b1a4187f101b1b172abc0289362f22e98f36f88467a6cdfa08c5175d9cbffdb4fb5aedfa73ddf4e2efad946ee324b079ad1d73fe489040e4884390d56201a16a7039a25227cd323f79f4f79b6742fbe775df7407ccc6f15b52293c239ec4f92245f383fb8614fd718433985ae01402911f5c996a4679834bb375836fe34782227d73b08ebdeba0909697b77db72ef2d654a292a0d1c0df00c53a48c7405574074a67f5dadda63da67da69fa238669199c024406ed728762666666ef51033777b3928ef1d5a3478f1e3500c9a16608911e7c3072e375ab98170cc33429b518334c0bd632d8e32584dd850bd757ca74d8923006c7c23416870049a0766e314ccaf8d9f04b99816d3580c8eed8ba403b6e7c3760bdb0d20033be9d665b0e59ed728814735d49a040f6f696ddb4f6434ef3edb319e2b7ed451d34f97fc031721344af86608008b33a9472c8d65e0d7466666666686a0022a40603447af0e187ce9b01025733bb034a66b50331abba10148420794733a587a66911c36213669d7921a0d4783e72213a2709e1f9c855ec3524efbc66e49eb7a762ba03c93be7814417b23ae76144699cbd9935665dd5198f7654c55425a4f3fd1a8886cba31d3d7124576959d6cf4918676674442d326277172e5c08218e15a43507c3608cda0a52a954ca5f56313a7ad0d4004408ef4ad8ca1cf3f594bf704e601eb1554ccb4097c2c2f11b6f2cdc557b75d111d3ab76fe208e6f672d7b3bb42ba3291f9a557e772a051b7382dbd1dfe8613b84980ac35adb0b957281fe027d053d06ba0ee833b086c622c351532d09134e31bb87707b2d09f367053146182116218429fe7080508ed8e2caf74e485fef0253d21794cc878060540189970b4ae64cc455181826747615e6398fd1ca1cbf1becb3ecdbff61de655e8e698d79ef15bb4baf988da41e7afc6aeae3ec55a4d186af5cb972e56bce4bf6b2c0cf917ebaaeeb50299797558c8e991e35c8af5d4c98d247891efc6e6bb7eb3217044929e544a5fc05478c8e191ade95dbd72e397707d6f25d11f0a5dd1bfb2dab7bb76f5d9926a59649f9c46ab55ac5e898a1a92107213543884033665dad7640c0c6ec8eac6e12a0671e6330fd5f3adb9253b840ca13ae58c10514db8280163d28c18f166900c10baef6156b5b9c23e06965c03746ebfa7cbbc4bc69a490326d0c6bf67c649a96ad564eb2d69ed0a19203951c6a8660a0071f7eb809022749e73184dd370359edde87443e531dbd6229618cf4ce3daf4ba50239e8b01358196ca9ac1810b2dabb0ffac816cb201c698ec6dc9734d609bffd09e184134a29252792d2697ad36272a092838eb402b75869c29775499a1eb0d4aa0ca765eaea9bbd5265d486fe0c42175223d472177422420f7dc8057d6200f4d07fb86999f55102a8bbd52ee859af7a043df4185ec143311e7ab4a27f7d9498e26b8f87be34cdaeeddb6545975f90f9584cc1966c165cd77a51bcc0d8900b6e5fb821447af0e1871b1c01f012d6d14adb31313306b2a4f4770c6cd5d0acf65e29526ad81da36f5fa021bb8345e4ed3ec0d6e6ed3f04c179e759d69e0da3af610893fbf67ca876e8dc731e3af7280f9c6faf728e48c3c90d961821064a34a1401137b83206169890a206ae76cef3dd9c731e44ee51213a173947858052d3b9c885e07cf37571dc719f8a9df31c57f7a998aa76d8bcf39acd3b67ef280e14a63522e7284ee722c7e17cf31cd77add7ddcb72ae8ebf24f05a98dab87b41369f7a1fd87feba5fcf8790240b594cab64c12ccb38cba49665195ca0213c5a8bc2904aa552feb28ad1d183a606204238514bd18ccbbbdeee7641b50b83f97e7f0a2e64a615d3344d6a52d3b06f852395da6c0afcead1a3478f1a80e4503384480f3e18b95996e1d6f4f5ae5ba423375c7747467f239f476fc3574848e443fad2ab1a1f42bc1ffd39e090be85b9dfe3db7ff4a0e1013d16a8f6582c7a7cdb70b479f476b8329cb76b2b2382a7b8397fdb39fc5ac475de68e475dc8723260721353ff4e8c12faf37a0ae43a55c5e56313a667ad4c0bbde2fa58b0c19ca8dde3964cfc7ca928f163479a69246e8b926a516176685a3575df7936db12e2e2f2b1c3a66686a009243cdf288779978fe468f8561bab8406f27c0b676675819e9ed015819cdbd665a737cb3beba7cc7f8c011312c2ef3d8bebd1ffd9bf71ea105529e3f5e8e70b44511e79113c5d5c2b0cb0f474c0e715d5c36482b01efeee82831888787d96bf3ac374f94297aa650f1420042cb11c322532d0933055b2287bedcc72efefa45dfbae62f6cf514c862177df18707b2d835264cf61f8260173b2f81acb3d34f98294f4ff9018ad203f433858779a2f0f0f0c428a38cccc313833618709b9131b70a2224f9ad428aa087f9ad628a2fde7747032d4c7b3bf6fa760e895eed73cdde3fddda5e0dd46004754b00aef61612faf63ea35bfe7abdb8e06a6f345a66abf801145e002db3554411f4edf0d5aded814b9adb0bd20f215ca8458751fb2484ce3cb6e61e2dd59230b9986f775dec9a84addea29bf0ed32bbab6a83f37567e28b435108b2daa5b743bbfa07b2da35264cd70e2594f0a4e795e36ae727232bb850824e9429b08581c18a93d7ebf522c2d54bbeb293ea3ff4ec3240175e088307b6728c70c5d51e81608b81769a0072b5c7a01aad0c45a16fd7a2c04133f6f91b687774cbb7331366ff688efdfa68e18a5fefc7e899f6cfc25461f6cf635f3f438e9bf316635c18f66d19483bade4a30560f46a3f7eed9f6f3ffd56e1832b7cffb44c655ee321d59230699aa6b19768876be65e66516f50fe7a84be9e0dd0bbb12cc3b428e1d622166d52e346bc30ebbb63650cb8e8ab85590ebedc7cf711bae60c3d730dba06bd88166195bd523e742e02a98c5f70db321b83ce5852d7878928bee94d3f08527f93fa7624f26db42b6bbc9e3d87f9489c79cda1d79827396c4d879e4fcd4923ea5109599a6f50abce5a4e6ca6424d3d74087dc2967428010d4c314615b4b8c2043b80c2b5f97a12a040167e9210818a1354a82025fab64f4256e6f29bb0a3710898d3cd4e601b3b0c831f742c3a3b16b908f4a64e6c1b1d028aa67953150eedd2db25bde167ba2a1bf8a3ab8248223e3a7f7cf9506c8a40dfaf395fa3c37d1dda397633914add743f06aff01550a818bb34ba86dd1d81950f5d3ea42a1ca2438fb43b324f9132c64a4ca6698c611fdf6037d86710a0e1ca952b4c060ff150a669317e1c049d90920eb1412e28199f11e34b1a2f2dc34c8a8e41e798eb18ba25e0f755f98b7decc56eee238066c665cccc39e78f9bdbd6759de8e42cdea8eb4c26af2e2678a5e45dd775236e3bcd6d9bd408ea37cfc877bfb577f28de3b86dd419416d9e725971ce0e448ea8731b2d437e7b7139d16a0455512f12d5d95512d7719c11d473a817a1b86de344ded16ae43b23a8ce088a478e8d5cabb21a41bd473d80fa225be7d649d9d1ee0653feb7de5a18d91f266918018ae1a22c81341a394db3b0d492732693a9e4d239d28e4823d25764841ac5de584e24d289c4715fdd1e96adbb9141c4d148c5426fc879169613cb8992485fdd1e9293bacf446bc9db6f92f347722683f38a72138974a23507a7ab28af9d9bdcfb6a918e4e97a7e87da351e725aff3923772cf6960376d702aca4d24a7e9ce063ec999e4ec261429bee7755d7ccf491df53c2775b4fed0ddc813e944f21a0489147be53989e415e73b6fe74e273a62a1db2bcfebbe12add3fbd649dffee8839e73dee7d19f2b4b203109096e5faa7b9ef3e739578928af261f9d4e1c37f25aaad339140b8bc96462992e1de58d7c4f4ed3251bf8dec9fb8a9c4eeca5eff455fe53ec8d8ba81b375a6eb4b4a050dcd6ddc8202210fced39d7d27223e54a6e6ceeb9b799689d71ca0e02ec1e0bad39cfe2a5afc62fe194aac96bc947de7db54889def4979ce492a5446bfc9273de7da7affef0a7343a35bee41d10b0e4d34fde390d2c7dfba7af925c3af7d9e03ccabbaf9a7ce4ebe180f345d83da7e9920d7c9487fa8a78ce3e32d19b7ecff4b1775f8ddf75255a71be648adec9bb71c36b5a6eb4b4987cbd437947bd12ad3f7437f2e49d82f03c8fc6d35759dcf36d9993e72a16af385ff2768ee54332c39f9ce38ee553ed90f216afb9b1c3ba922849790b3db9921b6e833a649d6e7ca96f216be4ebe98052e160f2939f6815e24dbebfde99becd4bdfa495446fbafbd6479ff7ed633e543bdcf094d7d8f016e7b8bb415336680bdd8df61633734d0df9551c26b8a9079a73dd84996d3cc6ccbce4dceaf06dfe55520634d7c705529e7dfa7a3a745eea9cc5ebbad3e974eabcc4e21d8af759befad9f04f2f32bb2526893a47a4f2cff6e6574f3e55277a43ce6f9b57a2153571808ec65c1f1744f131dd8a2ecd054fbce6385ac6dbd36173f6e5724427b7e1ebf96839d1da7f72938b44ce7d95667a9d79d1f41a33bdae2610345cb9b2c69f9ce4a2b936fc54472eddf4d90c51f94fdfbe8dafa29cc55b3e1bfe92af67c356fa8a6ccecef2754b4c146ddfd2c7222f0531b988567f916f5fdd1cc90c7f9252e4bb2d6e731590fd13ed6e307dd24ae4a7b7a9e4eba2d2e7f9fc48b48ee80d26fad6371cbc1b72be7324d81759ef5c762ee59c1dd77d2a20fb406cce39e7220742e4eb1b5defbeeeeb2ce6e622e7826ccf73bdd17ef375d1578d70d06f14097cee3566e6fc517e95483761663a7ccf5a893b2c642d68826806dcbed4961f665ee6cd2209451cc1093e406862c7c53936a0c21786aae069028c242e0ee3b90acf323c634c669d93e60727fcf3e2e9209d1e38e424febc78b0209d9e6cc889f6f3e291413a3ddb9013d1cf8b870bd2e9e9869c783f2f9e51904e0f69c8c9fc79f19482747a4c434e4e3f2f1e54904e0fcb508b13d85a57cb8f134ef0b5f5d18fc47ca436a42b5f973d1f363e1fd22670120c93bde3a2f875301ea500ce869f9d79a8b8c28347d9c4775918de2390760bd3bed45b9834be2e71c657d924868c0a737da080f1b5fbdd010c7607f4766f772cf7ebb303268fea559f80b4b3debdf6aaf5816208bf3e50587100293d0042e9d7e78929587e7d6eb044fe4ad9011a3d40e2092c62cb54ccd78bc82accb9f08b8e7df0374375c30cd5584db9b412988d3c7711e61a41bd119416384a59e714d38ecad4d7f89a63b093a9be024bac6146abff7a16635c6cd29a7a0cc33c3e26139dc330cc610dbd6aafd1338cd618234dc611093f4078750e86e292873e3f78e261377d9c40e3d707073cbf57fcfae0a0e751317e9156f8b19747a49376f0b9e63c3b747ee99ec2677bbec83a8c1b638c2c5ba64a39672ae5beea195ab1edf9e85c246234d29cc8980eb7bbc3eb7639d1eeb6be1f6b1916bbbb7d2ecc76b77777efee2eafd771a3d14683753941afcfc9bebbeb4286cc4c996b04179198ebabc52c61ae8f13afafa9a1af32e5b22e21fdfa9648d7e6da97a48819d7e64ba44b2412c91f545073114857c0cbc408db1d3a15221dd2852c8a750c83c3bc98ebd344d0fb34a1235b667d9a00faf6ce25db79897449dfdde184ccbeb89e15b9e98f14097c2744a0f9e96562840daf5ce1018ff0ef952b2ecd6ffa9dd07c3d1e68706974212ba680db95894d2b8ddf42991861c32b5778b4d7f5b847f8f7ca95f69bc68186cf810c8b80827122acc33e8e963ef0d04b0f310c1b01f6d08fd4f0ebf5c29c137dbcac2c834e836c0f5d588771ce43479d87f5ce499115fa1c578eab5d8abe2422e7be249c774f4c9bb5f2ebda4580e6ce36e43ce634100e117150c04be736ee2ef4461b45d2af05e9d7333d72333f73de1c1127f382c4e77cb08b8e48ca0b93659ae7434a2dc322848ef9c034b8852d28d1b9e8f1b0048acdcc17597d8e6bf3e805d1dca632e6db9764f322a0d8ccfc0d4440e6f1c36cc8f9e85b1da2c6d36b5ec31be5656d21f87686a279fc8e6023883408f6dddd8b8d2045d29bc7afa65fae8d262962ba761150570111e73b48fc1622f2ed5bd2ae24d28b902e11e54500e68b39371424fd9a0d7c0d8a4631ca594722b87da9d9eba17331c6c83db0055d31744bccdef938b43bd8a3c8a37316b677fa041f9b888ffc85cd2bffc8c841d0a1c87b07b63020c882fe0324699d4129a1da3b0f7bbe760f4697c8634350e4333c4663af228d49441fb6588d491e7a8d3adf1c93c4259149cbb0672f8c082e3a87f1300f8cf24f0f980be6441ce79bd9300761e65b96f93a84bef97e36433c7408b5d911312bfffc4621b8a465a447874c5a66f3e85047e4d1e14ecb701e1d36f17cabb8628e2b6e956e4557743e6388d168192bbb236a5276f408ff641e19f4dabfce3fcc23a36d85b9d81d90361816267a6c2fd3bcb6b230d16114264eafa207d1abe8de079d4056f4f67e70ffc3c244d77e8c3ed8e4833a1fdce95574d10791804c8460abad54197d295cd2ab6844c664c2e85e3f99982ff330bd7e7df62c4ce4812dc813773b4c68623cd1394a74ee611efdb104b3eed047efd77c58985817e8238fc75857cf3c30ba6c5ae58c1ee521c88a1eaf7cfce0abc96caf6de5635be955c4b02ccbb24ea35711be201566f4b6f2d3322a1bfee5affcd14a5b890e5fd1e192e850077b8db6e8e9e42e8470f7eb660966ebe8415330d7ab8e974d23d0e4c202b9e22a1bfed4bb24c13ed3be8a99e920ecfa6ef7946f6f8a93354ef40ef28660ce5455c3bcbbbbbbbbbbbbbbbbbbbbbb53fa673dae63ebd9ae0bfbd615bf75c17571cad757eb9d44cf6af103650b3a5041114a20c1133e5c0801075c1c01a80bd7baafcfeee929fdb3de4071883b4b8117cfa9946f15fe1bb87da952e7db392ff4d0b7d7ad85b0279154496f85198120ab9dc463be7ea3d0c2781cc258b02dec09132d83797bb4b23b326f8f5f581e9a4e6541af31da8ef669447cda6b61dab14f7be2096c69496c0cc3b6b4122cab3de66b462b1604c88ad89354ea74fa1a87b8990767279855f3ca3d5a106c693b99f6e4dbb5cc8a9c3d5a500f6c69afcc4a0fccacf42ccdace8ccaa0559f9765ec1ac9811be1d46d9812ded07b274604b3af9766c8c6d694141bb83bd1d8bc2f2e8d996c6035946d81d98b7122cc8b3128441a3756e21d382aa90029a3f5fb129fd647251b68c3f74c6981ee16762761f9f3df68e7ba230211840bd823f0b337a386561ba5d1c779029c6c3aff1c056f604b2dad90ad482608bab5856fbac1a4fbf7eae905dcc27a4c6d0825ec94bca431ff77cecb339dab325337a8d4030ca936c0a6c2dcc9ec0d66a05a7c06cca14a9d12a672a7bf20d9740563b3ff99a7a4e6804b3873a7a61aaf66b2731e7afcf500e6637315ba887dae10b7a2cc1acfc82241e5e839847a4dcc339a4b03b983215cb3c7cc00624e9d9fdf6011d139a35027dbb0fb8577cd14410f785ab3d0a318f7ea6f5480ab6b020c86ae9d9ab27f55262418c053d7449ca41329855aee0bb85dec6870dabf1d5e6a307c18e7ca49e0418369952a7e5889898ddd1420bc3fefb52319d6767eed6b417ba6ad2c20550174ab6b0820eaec4e003202821451323a438829d10b33bbaf38ee2705455d371187430e4ea3cc7e5798e8b55355c1c822c76ef8b4131414e9ef044e99902141413146305bee012a1216c09f6b202a930ab3f735d0de79e7b14c7abe97ce4238a33f2ce735c51063d62608be60acd95a15eed8fbe85634cf6ee9b6bcc3abfbd760d7e3ff6bb1b4cee35df9e35807ebd1fdccff48a59c7c70f694ccf60fad70eeaa01eda560e0fb0d8620b26487022892fb8d8dbcab6828041126428b1021d08c1898b1d9e311db63aa859ecdc415c8d7cb750d55e736c79c46c0e76cd653779b5acab5c61d60efa2017899c77055d93746a22872e9d137df2b7af7bf9d59867b8cc8d8df6b76fbefcb4afe2c812f0fd19417d0bb5cc173f897546cce0c90c9c409e5f691a8661580ceff48a25edd63426acfc8d0ff9f395776470860caa38e66768bf473001d0afe703a335e6355a63b02fe6fac8e089af70870912cfbedf640ac844b7d63503a1af10c219043d3b849bd7dd60aec71fdebed031f8cde7a2f7c3fbb1aafc5914b97cf875cfde0f11e5b4309756ad6a8e49d7707cdcc425939669ca4e90681949631c66611a61b66362cc762c0ab31d0b9ab0e50d4159c210a4d081bc039398d811667b2f319bd6606172ddc4e4169654185369990a31acb55c386182c933a9491464b8d8798a96591f1a80e1d9f4ec2c45cab3f34fb7dac55aefd060688c67874e602b0994f57132c54be1397ae66a7cbb60faceb67a88b282226a00c517bc70818b9d79b69503031ed46005599861c4158ae0ea1bcc0a993c3b64d22d87cc8579e4663e8c41129b83bdfa125264d8e14e1498e8162f3105dc7162e5d9898f93a167e726fc44d3480f771686bd999831ecf4071dfb7e1842e731efaff28fccb585a36af3f32d81762d64b5f6ed67417c37bd5a6ea7572bd8e22496c5be440c0e9807cf2bd85a57ccccf6f04eeb4ce884c226bd62f6686556b8f3ec3862681bcc853c24a03ae7eee829434e983b7a7a4aab6ae442166f9faa66c39190c5a3928a07d1e6225f978a87ce39e7a810d23b5fd7f23ce6952b820511a9826cdfbe7d17d1bc3deb2f125d65c352c1e4e7a3475fafa960f2c4c36ffbef87edd7bb4fc5c3c8a54b2a44e7235f97e853f1408570ad37c4f38daa6a36ce3749e37a43bace2555d54817b988e2885c7a8e2bc735a21108b27a208b9da635264cd6d971f2244a4f0c0dd44142ec710a7bfc61a1a067a067d7a230b7af7f1a08b2d8b9af83208bbdfb5a08f61064754fafd6d57deb5ad7d64da677cad95771486b6d073674c69d659d712f905e41e1b5b3f2548cdcd6dc1997c4ceb8b3ac336e202c80e9d1609af40a66477a6d01dadc069396a92ea5b700750bb604b55469116a196a41a3250d1b2f1b496c2cb1a1b349df59c118016607722d51becaaf2d402d4090055b7a3ed21a23f2ecdb562987d9812c29698d5e5b801ebabaef5b56e6a2af5680f599c3ecc09607b35361762acc4ef76ded0561bd740e2ef76817265b97e8dba0469de03cfbb886e9919d25e98c3bcb3ae39e3395725fd59d22042631a1e0e2075beb6ead9f83dac7bbc29cbd2018c7851826e13ba17bb9479eb917f1f2ed47eec81cb923e4cfa81312934498295fc5d0c0d66e94e976157d2e2bfab26296159d66591148cbb4b3dcd5177715bdbfdd55ac3f3cd6c9f819b5c1792c4631666af60aba3c742e876651205566756f198ca2fca565e082c141ef6eefe65eeeeeeeeeeeeee5ee6ef66daac2bc553630efc274f737d6700a9725616e8989978c0d89f0b2c2a16386a6062039d430efe87983103e71d2de0570342197f48d2e64613618faea127d6c20f4dd5b7b125897a4db2d98fed545873280c2f079828411689023c51a307082451090e093c4d53e5af084cbb6360b55788110d288010c4c30e46a7fd9560d27f881c24917cc20a30aae761cdb0a410f9ae041132736b08114ae761ddb5a32787686a0808211c06002573347d622bb58a95b2dd51b0ea73005c81420426a8610e9c1c80f3741bce0ae562b292bd4aa1b25c6aca88f11c550c0be30d7c541b1e38346cfcb5557efc50432c503a7afbfe5abd3451e3d1bfa4d7e62a12547d18afad2e933bdc94db408e7ec755212c58173e6f7be7d1ad87dfbdcb7a2f5ed798d1641c2cf48f82b12cdd96f348a84bf48078ef0b30771225ef5bd62701c1a62a050d8335aa47f502d12d291f94851c5e3368d4c25dd6ee7206f8f9a5909ec878e856199981f5dcc3a4acc63867d1d14d5e55be2c034af98432ab32f62195df5aa338f2f1af42c73ac33485399c7ae575373cc39f8759aefd7f81a4da182d698d0b5d5ba0ccb6aa495e5316aa4fb4873beb52bdcbe64eda457cd4416a3635f3fe9557b3f91edccda3ccd444f692755f816bbd6470a30bebda370d232fc82d92de9add38491b0c1accda3d351b44c744cf64f34428a28bf3e43787d8e5f9f21e8fc8b6582d0e9553b23d1abd6612566e526dfdea4652aeb20f1af0e07043cb03b9657116098fdc0baca0883977084062cc00b57bea30cb87d592d4c1be09bb5393ad5ac4e311731db6b8a6738c81c312c321b31612b765f573160a0810691294c61b62fc62f307ad080a301387a2c0ca4014703702c8e15cc8ae33ba8833a688368608bab728b0e370a2325224484b3823bd99531db1713123df3e4cac59e40050c843e0ca02853869edb2c8cb93e42087a6e3331e6fa08e1e7395297a17ae55ecce97ac5d1f78b79f9ad2b855a18d8a88581361e7a5500cbe668d7e8230b269096a9297777f74fadbceb7ae5a8a159e78fb9f50a47af72a2af173df2ccb28da95eb9cb9cb8a5752d6e4448a3d3a0f2b0bd8c62f19d58e22ecd9dd0e8102817142c523f783d7bea072ffe420d356ab0341db9e53c11ad3899f4f57c7811c86815b9e69eaf6783e652f39d4ef20b8cdcf36d9278d44568056d282626867b8618fa762134b04e4cd6e48abcf2ec6540b6e582e1046038c1cb083ada7be564d62983b98b4f9980d6c1f946636dd770a44813798d2f696def166d9e51ad0ab3a234672f0829e863d043efcf4884aff942047dfb103d2219cd339b19ae5c49a39de4c8cf066af4db031bf4911679811815bdc66c0fb23ecf1e37cb2886c3b2df601f895005212da1f3ecd15f446041e7256645a55ca0af76477f5019dd42dd420cb5acb7146435118ca665cf4e35adec29b8d19acaea0d967d6dc6acabc54786104aa1d91e9bd0ce71c38869abc66a66349f1009e48a9920333cb3c6ac739f6899908561e736d82710610c064e7bed0e4864617a48a1e2c53318ddc6c4e3388dde195a71926e61a059ec699c81c6739d398391d82a4ce00cf487b4cebc461e80f94d6fa260567ec2685079cd339fa1812cf6ec23025b429ac50e241434d3bc435f67602bf3197ef203f9c94c7b7c1295cc3af324cec099cd86af4048671686ddc90d2a5cb72a88d91df3f4ed550108581ea5cdd1a35f22666d1d1f8458cb62478064c0b3fbc02fccca4e9e602a52a688c28993c856666520ef24a96634be817637354b100359d9e4166230c320846d029dded13030a44900f443600b870e20e911cb0148181847af38a659ec5b8f990c46c83d78c9cccc8c08c78abf24b3b64e94d6691d1d1c1e29c7753b73b60e6409b18e467d681dd8fa61c9b78ece0bed7c3b1761c4608c11760a445b84cd52cb961bd59ceef11f701ac70d31adcbba93679d6fddb60ce6bbcef9fa9a46bebe6a1992afe36899e9eb313ae2c2789ec8510ebd8462c8ad27819c18f010b185175801e50b315c9f476be9e36f5871fee4f033d2bde9642a7dfc581785f9c95d795dbd217d317abbf23aafa3ec9c8846e91d8d50c0c4a012658c5e64a3acd1638c51fac26d730c1371954db4ad6b523af6ad4b2c7a3e960ee915e63450f3fd30ccb5fd5804309e8378cc23642f887ccd63740c5b6aa3bda6c51ffb516e8c45c1c58861910993e7209765beabbdb636da67187bb363be1f631f070d818e7d2c05ceb1dece1e84fc76aed22ba68136da778c391a633a5c3b8a69302bcadb71c4e868195133fb7a4cd3cb6154d3b63a3f85f2a28b6835f22291cb33d32af2a86118e618f47885099da56b996e49855953dd7b45cdd91e63d6f8ddc566b5b714e03792fe235a7b30c2a843dc55868d200816b96186514845105e41e0913ef8e1f5f1c14b66b6d6832176f9a0376cc8914e6ea64164c8912153890c3932642a912147864c25466628405f48bbb1237863b85bc84f439681ce5e49df1895594cf61a31ee3baeb62dcc1ba3a866b5b757120a15c48b45b8bbbd8d60633b9c11b32e50b633eb2a660a902a2b287ebe1d470fcc03f3760e308f0df3f5c83ca4af67dcea38afa3d7d8ab0c1d5d719a8daf2b1b3608f135c789296c2011f2355a3d60c6f38fb562c8060688e1d9f4141f36ebf322e26f34ef419217b936c673e1055a30e3d935cf862d638a67afd20331681046189ebd0c29168c279a10c6662184c5428ba5a2c56611250c3040a0e5355a73861006038cc8918568dbb66d1369a31a64090684b154b280f962a5051ba8149e3d0625a52c6814b42e7a1040bd46eb07bc286d72935bcca8fe608467e744dde6fde0cef85105c18f080469907aa60e9e6fc881c50fb98467975e90fe8a448a4d6edea88ac00bf28b97de0f4c8967df3c1b60148ee33891881389384e2412711c2712715dc6a8feb3b77743b6e40651099e9df382f078d686601f6f8ecde50737c7e62bd8c296b57975a18165a335e65b4431d02b0d02fc224ae40b13a347fcd7fb7180b7d92b6b3c7ba5dcf6f17359175b061e4192d9d0f022ca0ba3f976434e90edd11c7a41fa25258281c640af86ac945709be872f263711203e3c53625638aa98bd35090654b480ca2f152dd031c02f152d7022bd98ebf9884966f47576e955af70446fb325737d78a67c9d7124fc15feeae8d13218dd1c9973fba5e6d7ebdeabf6679e281e0efc90be7c61aed34ea536d214ca3f9ba8dd71647b1e49bf0da7c1bbe30812f998df640ffb33e8fcd9c78f519bbd72e523f5d182259c63d500c2a3d8f4a4e70ee45210a17861de167650e5515de6f9d8203ceae3cfd633cf87f7494df37cacffd85f1a435685b93e3b986207541ed52bd9f1c683edd767074f1eb58d9673acc32df80d9672277283dd60371884ee446eb0e944be63419d44a2af122149392711918805751a491389e4cf99e60de6cf3927495ee788e6a1137108892cf4bca3245ae788d65425e2515a53a91b6ca66e886c708390831ce4e006219432c541e87e83119142565240f3a7ee14af333f6356bbaddddd4db94b1b7f24316cd06f4fd0af6fcf6f907b49091c7b3f2aea453f60dd749092a341a0b743230f5122971c05c10cefb9f4edebaf21bc21e73d2ab7a337e47c46bb25ada5ba79d36ae48da09e49ee7d15f5dec9b455d44b1f6d9ecbaf3b99e49c23a781de37a2b584faf9d512c93323a8afa29e736e0459d856873cf4edab467e73d46f4ed35044ab9187de9c4b88f9421f51cc3d5a8d3c9499537a67ce7d46503a684536dfbac7d6b9c8e5ccc7b85015e79e8f1c3a4ac55120fbd347ee397408a92a73ce37af246f9f1f5565aeeadca3aa4ebac85519b7f9e69273e822177d5525822208a17350f3f51e2b25f3cd4531f5e55da84a7a87a22a4981ec8fdcf3cedb1736a42acea5431f7d1e5571aecabca3aa4cc549e92e4232c30391f3d2d737dfbeaadabcc7af7ea340f66b8f5f5f7d8f5f97df7a453d127e6c94c6179ba39c437d9385d6f82cceb944b9e8ab382f72ae6187fa448ee24acef29150df54ed70c3537e011bde02646fa4e842d6892ab1d1b27dd145175f74d145175d941a9bb29df47e786ea295df44fa2ace934e55e4f0ab38a2205ee428a7e9e9ebfd90b4720e4b2e3f123cf97a503a8a9ee8c825ad904ed7bef8a3af428faaead6b9e6db27617ff715c96eba087741c91cf362d6177973319b8b29a2d5bd98edf2a16b9ffce662425a1ffa6e73312ba437cdc56c0868cf2424fcfd3ca31852e27cc55cbaf61591f4a65f46dfaf7222879a74516f241f798de71d69f3d14617ea80dbec099323adf7a3c6f57ad35007110da2b93396f322bfe9175110ccf022c7f97843ce8b36979ca813751413b9fc6efaa5a437e4bcc8a18b441bac380f2945c2cf0e69c5f91bcf11713f214adf1c9322b951d5ba2a6a32dbcca3a7a48bbb9ead47172ae9a843d6de3463988c1dbd914795745cd33844ccd93a6ec175196b4b81ec738e398b38912aba2af3566514c87ee6aa4881ecc74c6630464d85b9fceed76de22391e181c8f9a8c268f7126bdaf56a61d32862580220d833d50ea3ce6b3c178dbc73259e8b32a939ab761839e7359e6f23251aade99ce3a812d1461db2180bceba8fc02d4742d25f64d959e49273fed6396ee3441b5d0d4a8dd69bf8b01a7179c81b739094520b2db46062647f52d00e6977832cb2c88283346602c47a165724922e72259ccb0cc90c0f7d87aee33acc3757c251872c8c6a5954ed20f2cd6ba46b22aa4452871a75c882586081c510590eecce825be7f653ad2fd5808051c64dd2cd1cd332bab46ecf63746b64e7a632a1578c1a71a991dec4871409fc9c8cd90c6e5db8f57da90c04747e2ac6b0f2fcda7124c6612f15558cf89b2246b6ca47ea81ad52e521e5305e267c19126691f5fdcaafafeb5d65c9330efc7abd0ef3a3c30f49fc48f7ab39af0a22fae8013e8e006361f66337bb06547e7db6d079d6afcf164e7ec2166fdfbec5ebeb0cebf3f382e7badb7e5337a813584d6435a471e30be6fafca8e01d055a08452b4430a7e99d025b983774f8f303041414c4423a30569ca15eb510fc4ed2aba6f007b25a89593bc9b777929659d22d8f2f28842550591f2da2fc6a31e5d9633784d0f3b18e84164c5c01e5e612ca8dca19272633e74387402d087b29b71f9218042efc3423263bff664998f1978a29f8e0d93952968489fd5231851d3c0b00f5eb83041e663a6356f34ccba484718686df764f8183be63d5f8ca2e23ed62d4c15c07435b28a486b38debe2ca46c2648f2b57325abc3e0b2b90b5bf7d2e29396572af9cf4d7a78a35b2e0e26b2ab280db213c33760514165021410aac50868bb1d8c10c5840c5172c60c2135c2bd8ea9f181d2dc3f2ecfed232521e7a842ea728f1d03b14dce0a1bb4cf9824c031e2601080f1d07f498133c741d3f2ee83d5a66490f1d88d043cf418812ac3cf41a1bb8a06380480c1e7a0f2dc3f2d07de079e846a0cf156820e0a1077183878e0377090e1e3a3381eb43458d87ce48402cbc78e8dca46516e8a14f164f3c7466a265460f3d7e7bb09b67204725b874bb3efcd02d040c41091268e155c6145d70450fa25b394488829527869082204ce10a57dc2b1f9d9f1c31c647e72754e08ace51b40c74eee129bb839f8f584214603479011951aab822189898310331a4d8820a44b8e21a1f8fb8f2313a901c84c0d674451f4264598c095d6e6b5da85ea5b2b03f9f5f500a5ec20e908999ba81f90c0d27674ace949ca9de81aa1df31c8ce2883c739c6d35177d423e55cb8f46d5bea99aaa76909e798df40c074ad39accb74cca281dca6ee84507ed0e6c6761a44bade7a5d3c016e682c22e55e639ae922aa342924c37798e8be40b2b0f10c8ca9ccba457d68170947d75e6e593af988ef44c72de65a6aff4614e204bfafc48d893972eb730d745f20959d2a1d434949a919328cea8a9af4bba6caaaa1979fbc8dbd7a5f2a8f41564494c67473ae6443af644fae8d39e409674ccdba15d980e6449d79830e10f50106ce5b8a4432138241d5a5922bdfb385ad98974ec09c6231d8b225ddb91ae4911ad5a948d568de7e5939798ce4b97b488d693b5880b1883c9cc9cc27cb54f155d240428c7c56d9ff6d1a8a28b3cc785b33052d50e9a6f5ea3f9e691d66c2ea2383190057d731ce81a74412626464b0be3ca38961ff6c575710be184ec10ba843e21ec2805d7ce35afb7cc17406acc408c35be18021a6a8421cbc9c111c2a086174918a1e488be428b35d080a202269670795d20010fa080b10434c270a5810425befd654600bf3e5670c18a2990a0ca4dc52c93b4ca8f3146091b8bd139e6604374b7102627352945bdbdbdfa8ef99ee1cc351ab34f661946abfc69209a65ade76c1ca7e884c438c13e2331af654e200dba542a76ed28295d6a1475243e468dc8a6e2e7a17331c0752b0f1768ce2c508c84af2190051dbe608b8598c52ee59c4ca30c812d29e704a240b5a33cf4651ef2b5de10b3b25013cc637b096c710ea7740b1e4e227c854247640c819847466110e7800e1d3a61773085525818e8126817e75008b6f88a65314fe11cec9095f0cc424ac6483d6142a19882871ec9d816f664a3151e462e3c8c6a605e31250fb1143cf48ab9e0a1ef62327888094116f49488d6d49c9810ac5890462b06f49156ec079bc2500a3028e3cfa0802f98d9925ec1ca98106c653ccb821e65a8e7a167531ebad0d0432b0f31a10c8a8581994ec62483e2a1472b73bf664bb8670bdbea615942ccc3029b831d3a5761190b2b133d0bcf2e148687fb042a3ce45ff78865be543c373164613040047eede2c0f819532d53a1b3a37a95aa53ba30adec10621ffff5faed612a3b6736f320e8d9dbeb047a27cc66ef28b3bd6e1acf1eb930db39898443df431d51d0cec118cc211820e2f9d2f99302821c0437e65b054ff8750b2c0f66c22a1cb222ebfb34dd3b369cbe9f28912d398ec24c984723c139da2533f1287c41563ff9f68e56a735b50375bec1f8da4c446927dda47790601e9dc4e6e8182c4cbba4487c3b6682c958e67ded825e0ef16df64a867d303d44e6186f8e8883f1aea2631f4780c9e8b146c8c3b57482db75b93ed753ebeb006f97b5181afdfafcbccce0f6a5b2e4b28fe3c2100f2c7ad0d3eec23cda5d96055dde2588e5978a264d1ed23aa9f46adfe54d9c838544290318aa620d6a25a60aa167af01c89367cfa165e0b3d74448c40753189e1d6532318f8df3f9a5573e45ed16c5cc0973a570610a4ec70dd95e27ebdb4bcb6a395decebcd85e91e33bd5a1ea2d7f3733385b2322175425543c23e9ccc852001e61955f1a00991b9465536fcb2eb05b4af2ba3ec09c1d63553debe6ad8900932c608bbbbdb65cb7c1331c5771361c5f7c8d5ddddfaf614cfb7bb100184ef6ef912615371c5b777b7a8d8e2db5fba63f8f5a182078f3d9149f1a400852b4f744109340882abc1400226bea1ab7b60e2043c6278c1042fc882cc3056cf0cbebef4442b4899d651c3daf908667c7b4c75017e7d8e40851ffdfa50a1f3c3832c06cd031d604f60a532188359d5ba960d7c60c50bccd8620d25b8b21728e9220b662c71c20f5cd917b0a0c186c593a85123339fb98e9f5963626260abcbb038e2b32c662e61c6dd63df0b6c94cb8d5f9f2e88f0f3d7a78b257c7de9c168c02fa8c92ee5a8ae57dc3e7b9ace1f7e69dfcbaa513a7ab44ce557cc4ccb5891c5b747d93ea1155f7cfbcbaa3d86a609df5e03907621ed43da0a297c7b0f2d837dbb0fed3fb415dda15c823057bf3e5634f1a65f1f2b90f8fab2820e128a4258c4ce28a3ab74506ca11612ea253162d8621847c13cb0751a8966d24b5a05add33a3e88be5f8ccb3a2216b199fe623847f4f67c2cc7dd7ed1d7d321019c23461dbde24f47ecdd88849e1e29f4f0f440a1e7a77654e036052511a2f8f591228d6ffdfa4cc1e4d7f39159316bea3b665ee1db6750dfdd55f8ec7cbb0b6ab64c453d3fa0c7caaef6eb1c5ad67e950fe8115ad6fed2cab45b35eaf7574ad7537030c208238454f20da66cc2645f661250f1faf5e9117a03fcfaf4587917cd01d231c7d10ef066618e6111c3d87331c1d468aa579863de987760cc085b1bd3517a9cb477aa7b087d665ae1a1bfa4d278087d5f2a1733f3f18c8f3ed3a365a0f74f8c31462344f131fa7ad0e3f66cd18459a3ecdadbe1c26db84ed3fd2d906c67aed7c8bfdf7eb73b8163df971a0393cc941ca3c52e9f22a4f1cd1be0202c022973106e7f77a9acdd778c47187ecf23c9d80b7a245dc7f7bad1ee38c2afb7a1e191647f84871e89e908fcda7dbf5ecf3ebbf6820852c840b1689a974a2c2cbe9e0d161b9a7736e80d392d5a8b8d1b2c366c9041060a95baa152918142a16ea0503e6a7116161bde6203e59ea76cd838a57ce479fb373e1b7ecfb99467327128efe34f7d35fe447d353eca8673366ca01c153d1b4c269409d5e5600c92779fa74d4e1b79d53cef6b718df35ce35cfb6ca06cf05cf337994ca692a6b9c9860dcfb5af12790de59c7b25f7a49cf354a2de57895422cfdd2879f5dc06ed96989eca739373d4d3386e0c13e71c8fa1611de75a89d3469ee6eb37beaad11bec3b6fa41a2206c75151289d43a91dc939d2181a89a47da619660000006490c1c545a58a21861b3752291b365a5a585850a8d3c9642a95e624914623cfeb3a8e1389b68d85a554a255a2bc6a3b524ef2dd1d25afd8b3b80d12add99b7ce4a96f1766446bf69aa3bed76c5094b37cbb30285ae39b7c57a6e426936b5e22f2f5062b7283bd562a9568f592e6af75ce25ba14878e7ff4ed3b911214aa984c26138b771f8b9b66c93b2f695efa506ec3357af2f8a693c9b9538b9f4eaa1619349473dd953e94afd7e2348df2ce3bef34ef5c6b71d50e2a950caa1b2e275a715e73194a5fcad7d3a1f4d5ce51b45b627654d5b9c935da42551d0d52f29397fcd4cd2aa62a2593a9c8ec26ada752c7c262e3ab265f4f7dd5446f3a06c7b951a5e4fe259f25975e6a69c9d95e95e8ec3739476b7f8ba764107f6271d407046c711b3e2a95fcb43a4d6fa93726df3cf5d96857ae5c59e36db80da7e9966f7f648322e137b9c9d939d357720ef5f173d053ed70c365f00ba8dcc503c2eca7afc56f7c32b8ea7371962f7aaa1d6eb80c5ea37217e7b00e158193b7788b2b51790bdd5eb178bd69d3d7ffa6af5f06bfe1352eaeba21c7e427194c7ec3853a6499e8f66aba2af66a3a4d9bbe52a934bf7ad3122786ef75e3c6a9bd2f32eee32f394dd7b8c3f35af2da2ae870e06ff1ee6bf1d211fe18ba256611cd4d5cf7955cfb6e78ca4b282f995c9bae79f755d4775d37519a97bce49a77376edc70936b5f35f29ab76703ca4b6e9a27158ade90f3b3e426d74a5f35f2251bbe9e0e279fa6af9afc06ed969826aa3279e725da1a55996890934f3ff92c6937fc2cce7137ddab11d49fbc730e0807fc0d7f078ef09f9ca63b16e78e685e62a175d21345c25f6af11a3ddf85a99dd756c1674e03539f8daf96b6cebdcf09112871284590f0102ac7898142e11c4ae51c09ffc83993731926aa0ae26f727f9397fc865f15c4e4d24d2e47fca7165a73bec54b9fe9ab4650ce42ebfc924f477ddbab49eb7c93c94b1e432b0647a16895317cdbac49abfc5235d252a48522e13ff97e3582e2ba3b7dfc27166f2f88f49373a7cfe41cd7de0d39ddf2f1899abce59b0ebd169fb4a2bec56960a712a8740b978a433d0a9942332302000000b314003030141088c482c1584015f73d14000d90ac4a644a9689a32488619432c618628c0100000020003333a40df635ccc233fcc116b719703863a4f80251cbc14e35e72992d4035737703ead0b2e916745c401ad68218c57cca910c0764da566f61a8f401cc5f356334e159ff80f6eabe5b15f30890dc1cda21c5f51cadabe5a45c0a77643d48263140444cda0d8202007e7a89d99dee0e31dbc559238142851403bd2b585ed9dfef6e785c8740c86008c9ee508eb00f8bc36747f9f58cdb1d88aaa3d5d313a73da8b1aa6b1babb0b5833d690ebb694b4f81d847cb2e39ae224ac9cfd2fe886d845c0bed2a1b2366b7514ec9e467df87c0a42bd59af28a280cfee293e79b23a6a9ad43527212b46e1dc1a3392b25c6cc1ab534a487baea1bbff245630b91be2e4a4f940de6b73da1dcac182e0a377c5800056fd98a39f0a98405f1e420bc1b104cb97513f7bf54dbbef1a6c8d97305cfd91a2d62488e4a6ec3d7718bdd7e463d512120004febd1260ffe509c69515d059b20dd428b298e1dea1cf8e8423f361661be94596fb4501eac3f5a577cd205d3fa8ca6f99ebaa4f2ba1cc3ad2f2ddad942f367d5a6b123d4976aa17794a92d29f00f2987e3c01d0f58efa24fb26f01b262aaf4979b91bbbac3599cbb15bee1e7b984824d0c4e03061051afe8fe10fd8b85eb78628d28c678290dee5f04b3a6444eec7b8d1ebf4cb0174b1b8efbaf01fa2c74dc38969e9c42e249c7c5895c99e928ace0b65f9a54e610d20ae42fd8c6a035e6001b3914cd8a5f0175a68a226c318296c91ef37a6cb277928116dcbd02c40fe219aca834d1b2e94851408a6ebffde053fae2a285804456e47ac1599b9360123687214652efe84705e7758785a854b2dafee08736e0135f2ee20473dc8a40eec54feac0d7bf68a59b231104a2998a0904d9e17c3ff679d4f3beafde8b910cf7852c96b9ae2fbc2f9e1604eef8ff0e36f7b06b42cf40fc911b487bba686dd11103f7a506beebce25b9525596c00a160be77db9635647a3d810af447473ebf1617d1cc84b7a48274513c5ce05c2ff9ed435fb2b205b716ba5ed14bd3ba8942823501323bfba2a317c511be1ff4e40a14e01a43e651f0ca6029882bb98d66f0a6ebc9d660134c11bcf8e82dda9ee9e81e6f7498806cfa497d3248d5551388e0f48d098a0dc8f9c04366e680f6f55b62b0fc14a70bc627ba96966f30e5a39f63d3e3c0a1f1b55fa9733d9cfb5bed618ff90bd1d7be223c1ff492604f90e3b86303ec413ee608902bf529ed7895a82e584cb5e06ea66b3b746314537b3198e2e8ad39617ea8e7dca0d7165d3f16db733430aa8a61e2fff718e32197ea9e975a2988d18c16d0aaaf1891099e431e8e5658895b7a62dffac46cba0017200c6a23f0f070bb67db4bc30bb0848369075532a10a66535430e9c98a4e572bdfad0983300803ee8b82daaa99ddf0a7e8e405b64d0403e5d77feae23a6885922b1f23e644444bdba00ec03cf5965d50ac71b38a2d2c042443242d9ba60150d8c8794bcb82cb0402b4b10284ca3cf5e639d627487b102b26ea2f20b6b0803cccc33844a69c676e337b09c6facd1ed31826568c00c8ec1d5c806bd89ef10d386488f254b0386488bdb8463b012881083d3b81ad40fb79d052c5f38d911c9999318cabf8728425a9c44a483eefe9ba4413d4a783a39cbb46ed52a5871210e4035d191ca4e761308db389002b01343a9d6962d81e1a989e6ad551de1f4a3dfe1b432bc510e7bf72a066f1e9ba17ac0ff29959790abb760c063c4a3860419c3df0ad666ca590bcfa87b8e39d123f8ba925f0a92d399cb97b072026bf615a1228a40c3a96e0da86317d3e873abe3f5e4e2bd46acb3e6aa1834869796fe846163dde9bc1b49fb01e847eced7cb545d35188d75c5158169ab061133c89a5be68ab0605d6fd41e3c498c1d899b599aad159044f230a08d7a0bbcacae991c0bc6b8732c3974c0cb6b623aa0da4c397b47730fb280102779ea34c72efb29b86dde28a58f4ae725934bb73e304884842140887ec8421228181676dbbf35d634c786e635c8f5753674963c5db3b8f75305f4e8be5c6a84c662b3dc103bd47a9b2845f5ee30ddb4b9741715fd6383374f5d7a4e12255f85f19ac411959715957b9174bed35900d10a1e22ac66b73d91ce3d4c71671cef116a5a61f0986033a900b7cd3e6af0212753e1855653214d4312910fbaaeee2f5bd21d5907691651068427a83c61a20c3dd15b5ca8833a4af855f3ce98f17e3b8cce96400aa8f7767cd9bcff8374ec7818feaaec0ada9f2c02d601814fec4dd91e3ed098e29f8ea040d7a99f003b50615f8cfe22a14169b036deb5949cb3a83a123014039196c12c00cf2015af1fd7910268f5c3350b0e56806e7700101933f87a291af29a8bdf7f4833263ecce2d2382c3b4c9bbc352ed18b5e1eb7c2061f12af85aa2b83eeb35490c3e0e91ab83d15aad87fd605eed41442264c426ab80d7136ff660f86bb1873efa01ab5846c4e3af78e25754c1bd9a66d682e0cc55a125af968dbc932380bcf7aa69b721a98338da3ea1ecdf45abf6aee73c437cabb3c680b5ae5d7b4dcd95aa763a686836c61b52781fa0261a53580328a1fab6bf61bc37b733a30c798e66f0473bb8c0d830734b5434c2fabe28994a0170bbc97e12fbce47735d0922884ea687dc4c2610018558f2b9daff94847db795afe46f5694082e39af7b3c6a2adead2a7688deb48c0e482f63befc81859a151f1019f2f25a33ef4d7ceb79f22862a28fdf6680dc03c7c101f2858097c3e9320358c33a841662020f2f77ac0ba252322bb93906b9481911783ca6a42cb5c4a567526371021a8a6c9fa1af8ce5e11aa64303c88d87738c81ed456b373d610092e888b0d83ea5a87ffd1e5be52edcd302b61315a90f326c843a187f3b1b6b08fc3a2f250c24cc12cca099e0a84c5e22dc382f9366c2ca5fe07b07e3274ac0b94a555e06bf66ba1b9a497a6bec59cd0aa214ae9d41e2114cae98a806adbca8bf9c08de2f1a42cf3cb4eb55e9e8f75f5e8ffb7fec278ddea403393bc79120c71b1232b4ce06db3cdfd0e00797e9481c9140c83672ad83bb7d89af58d4baecebf06ad0607d26775a9dc29b28754d80590b43894991469ec4421088d03b50392d5010ee1d11c203a0469ed3254a3cc31998465657a1d6bafc638e4a52c8e9143c829c6e93c1cf38eef47da188c28ec9d405b3befdd71bbfb2d2bec2269d54fb5efca708b3f6f556abe711bb4baeec2fcc747e951789ae3fcf51f68efb47fb0bf854124346434a0acc40e0bdad7b20480ba18cd23f84cb241e38f8881330dfed72916eb96fbc79258bdb663350c9a13346557fb4d0ef063b80a56736aa6e47769221665ab17b1581674c1e0cb3863f80ea2715ce2d65534a4d643242bfabb4f2f4f83e27edab1c45e1ba5df51418bfa937a5932db51dac05a96d8b3f8c4cc570980db6ad89b86e51da7240d2828107c1e111656f5f0ba946b62bbb4d3408bd005875890ac041161dc3f50e88f8d32d3f276b61a5d60fed048147b4f5c85f174811eeb50dd3df8b35d9ec32f70f9f61204e21a7f8d826a198968c3cd790daa01457fa33ad001583026428e09c919a13f067430ff70ff6e1c34c0c762040b65df49987ff9af1d3aa9f9fd864b57eeeb1f75ab0a547154181cad43c1d09d14794e8045045793ccb10f3adaebd990e33739c467b7b493c54b7a97a9e278c2b65514aa1f90bb8bebd1112eec40b5d6daa258dbbab2a66f270eb4f8b3c9939c38114fb5ee927af2b4f74e1452ac0bb328902f30f838df84baacdf868a83f5cedb646b95e1b945c86887042085fdd2554d3fdbf6fd69800412f1972bfaef91a1f418858bca71ee52a00b332ccbbafbc44bf12aefa2df1bb722114b63c4814b8a1f30174c4542e8f59f0f8e7c484aa36d07590b4959432d3a17bb682382c38a7c69a26f67af76006f4cb356a636ea313166966726c01004563d21e3136bed7d97b49fda8d21144d63e36594bc87168bb8257343d34d19336e1c90da1f7abbbe8dc6fdabbb009f1759be14fb2bb6ecded1f89bd564c8285788af16205b294ca7fec476b33dc51f1811fea02157393b9635e284a4c6d436d2ea39689441fbaaad07b6047c92aa192b131888f00e21594ccca9456c7d51983f9ece02b25b5f229bbf3ed4e103e2117088cf85771f516eef21e5950866b05f640a28c5265f48bd063f8edbe11488140f977d9fca10f9f09aee586048083251582bcfbed165f61531cae0363fa04632640c619816754d6a84030c0c1adbb320ae6f9941dd11ae1b3554e5b26f492c6d6da2860c32db12c4f68cc304c52ceb1deed04b2ef196dfe7e2ab656ec7bc2f995002916921c8b3b51aff4378600856f4f8e022da9ef1912a1c211645d7a4f08ba6219f8468ca1fbd8617ecc341b7d490dcdb49309576f00a9742a049c6a9bef1b88b1404e0875b3b8f935708011b44085057fdf050fe708bfc36386bc389f8fefe869edc2abf018f927663037d76c043decfd861961defb4ec28d57dd67f51cd0717a667a2ccd80f96a006f07aae4f337f34c8badf5d95a44cc8cf76532a45fc2111f0ed33e02ab1a98bb0d7a17575f5f13940fc2ce0fe76be0633e2ccd279f1c0d933de72452dfc19a489969089936351cfac97bd502c69b329a35a71a91ef30bcb12d625535ae90d5a7dcd49e083d9a4f436ab485bf4166d1cbea53ba22eff15871771979a8611b34bd76bbe0a03862133b9ac536e447bda20dafafa70ee2e82117c5a301e5be8031a4bbf9793827a1dabd0d2ad856714769aaec7094a4dcafa769f5287d16d18a450fbcd9d4971d23d80aefc5b3ea8c0a58303060710fabc0773b754f1683a8abbbcb2e5c0d0abb4e2df5fa425806b38d7ff9f96a627b78ad0a2f79747d42e92a33db61a90cd80d0801a7b702df6044614eac37f77b612ece9f47a4ac8325a7cac54a722d464ea3ffaaabbede914947e13a59a6e5488c9a1c5eb2ee6b89012a0e30aedbe039897c22ea03769cc880f1d0200ba95a6e337e332d82cd272f5ef8c2e852f076b4c3a5fcaa95b9707a5cb689a8028f479925edcd40024d7e6d3b937763afed4e38d2556f1426b81222159f1b3793dd685bf73e6343bcd980dc79d9042417f3e119c1765548c9ebd2ecdc2745c316b5b7a7755228f032bc8e11442d6bf46a669a4a9c866a2884c2cf1db19bbb4481a3a294f0959dc281d73a7f811196174d296a1ed1a1afa315235f99830f658687f35d6e1211e3b25ed58413eb299202e52b26bd55c324ccddf1a9648dc589fc54146245c5a652574295971dfa6f962d6df53757c901045ffe2f7f650a1308daa69911d1d8a0c2ed5e20d7d068676b7d0c4e942d08134c06bbe05ffff23100a840434facbd8526e61c7f294e8bce2e9d472d996adba322ae5d7387e6444f31ae8d5c7e3604f0a61b3d0e635f2108bc49f3f0c5e24dce582b0dbfb40cfc8bfffaf0c0c3cd83291cae8ac7c7df31f61b8c2d94f89dcdca0379c11f59637346742f2b877b88be6f2e552ae3573a50dbe2079af0c25b5b399af4074f408e9ea57e6560f57ed698d40b0f4d99469a4b766ff087ab981c43d04cff8eac13709c84b87fdf0e7a63c4fab9b9c8a683e66790caf1bcf9b39cbae758483af48b7525414229daf836523ffe04a5c1bf4c527384826d910dd18647375c9983456d7bfa120da463ad875cfdc501a2dfdde910c3ce0e8b6a2e7db00c612f3f434d7baf1212ae41c4bf73c9e242d538dba45c4c1126d1f41b17ec398d592f4cbf7c88c49e55360d769c882564d21206fd0f6d66d5163053504d6a17fc95a7e3e4176cf8e102829d8b1b9061ccff25b4c744003c52ae2cfe85d8295d841577a0807fb3515beec912d4116ae20fff2ce3f98bcfd1165af76d28a343236d807e192a59050395ba7140fe2b1a90b3ea4976db411dbbf0ba13b6c12d2c446bf254d66c650544cd06ec361f9c05b6119b299ef61b0976f3179c83e5069fd5116367ea073216e3ab447b586bc3d68f18127dbade2a54a75130450e0da013a90e5cd0e19ce61f88d7d20c35ac78ca2a5d6d2550b096b3142d82094e92f5a5aabd826cf1df81cad80b2acfa800a370d4e0d9931e40901c9e7c13aef046aa4ba2728da74659868da9998e2a44c3e20d532a228367cb4b46af7d80217e6a2b4a97e7315f9680eea4bd08370a621aa7aed83f24b1642a76136aa54425036af3208c1621c05843e049dea603c1e7a87ae4a3f6063c2252802b8d0f774ec25d24ab50999001fdad2a5fb0aa359478f06b9abd9e7644262f48ff5a38734249c79afb866f3043d7a1e2c465447ef20053c85ed5f86cdddba55bfd8c3a4984443afcedc211d86a1e9b57e0d7c6be6a58c2a99a7ad33e98b22e27ca677a621420a6d90ce4d69420d000ddc6cb4d80b3aa0fc9259ba2616dc7468ee8f8e0a1a18044def09e153efb037a9c98a9546100ca77e8c93be081cd97a9451e289311b33c945763210ff990e6cb4532c3992cfbd34ebe1f0d4be3891a60375e15e090070f8bb0f0a2718fd243c1d07ea6b485c2286c5e85d73abb3ccbeb8b90f3ad7378ee97b143de9f81b99afa93b596f58b32e99755d6aad4abc8e19f432263200fadd5fc5825c001cf618db5e883bd8912cff807dc9b133659a44cc9410a17ec68e090dbc0905d640ca13f0b1338fd903b6fad61159e93a0b46a5fe1484aa0033d7b5f44b00ae5bb08f62c1bc0b0ef750b61b814874ab100fd212d1418725cea098b0e4fd5d507c81d4fca9e250466cc811f6e5104c96b13f1facb1f1c4819691e45e52128aec9e942db6253eb0748eb7dccc2ea91f729f1ec4c1a9dc4368075893edc31047b317a92086c9bc293d0930e3ca4ea9b9e06653bfaa9c82517fa8aef8523473bb3cd61167bcc92dc6ff86242be8667ea14c723a2d7ae62d45c1843cf35c745d26b51c02ceb6c64f601451071bc3cce36d3f99c721ff42349e68110282b76904a021253591f9d2246f110c505024568e396694489a0c67dea32611c07da520a71d38e884836e1c75c169271c3ae0a84b0e1d70d005a7ce38e9c8812e8e74e26c0727ba70a68b435d38d881932e1cebcaa1034e3a192a7c773f5a31df2213239d4856ced3deee222acaf2700d8f83c6330f0a7e4792b02dd2cbd8d9d44dae2f85f70ee2fb425c5d478a8ef11d811f6d0c860117fc70593416efee8ded101107bd1810579406893a801bc1affc0c7b772082f73827d01b1181e294b95212407be8d4535bf4627db8b9bc4f0393602d365bc68185f1b01315ce6ed93c096bac21a4cced5bc8eb23b054f4edb80edecab9df07fd9811134a541fbece8862e06f1eac50b2ea090e28aa750363a03197c971116bf58bc8015a676df1a4cabe13cb158364754231aeab5858b626b78cd0501de9d5f2728993a114230664a7a2c6dceb4ea8810274b26bdbc75bf02ad30424762187fef1d5649f1143d735f2845db0681522e3c7f34bf03249bbbed9b0394e6a7f397381bd9844a4456751d3187e02131f452e5b7ee02df48bc56dac29c98d58c480f66cf948558b1945ef334ece3e55b32d2c10053a265035acc222978398afe0e84ca04098889d020b0d33ce5260e992fe2c26a4140e2fe652f92d13b554e5b510042762819b1c2e41edb3c12fc330daa00451121a18c92e0cf773e1bc329f1c69adcf55ffbd257a4fd19616b38574f354ab9db08948dc8771b8047427018d33177d7881ded4f12ab9e35543ea1baae29a9ff9001d3e79f87b0bb4f21d1b8ac7cf02244fedb5c8c0be9d4a0cc9184504c0121f41421fb20f240a1d6d68c8f844ba6d8c601e183c0ba4b8c6a616968c1587c64b34b0c4c7930bcb2c18fc8a07e5efa6d8f8b710273237cb3feb254ce1d00d94118c1b6609d62eb603a85faa46e8420fc2ffe135eb186b72f2d293f466febda5a2ae62abd3d6d77088fc5c9b7ce4d64b4b41de4813172d730a7510cb40e5872832e01d24c283a0defe8c111eb67285451347172a5851e93763c9768cd53377e9e2d0964090d0e42a9e8d3b633ec7aa35b75935925b017f12a2d1d85a092d1c8dc101beb3c89fe3ad4da448b1f375d7e8b13820342ad0617b28a2354ddb40877ab025063599360e0fd60f2ec6de65826dee30a8da5d171e493e66e44f6df6c299cd460408128e85ad007050bcae54bebdf8e17aa0b615fd54d2ad649ac99dd177479c50aa104a802e3027d9108feee31d69b442caa400b42dada68aa1e1fd482771bf1e1dda31cf535aef00cdf9bfa9386b3ce88b68e40e2cf33e9e1dfc95f1843d4957c1f4f1c965479a15434371f5fae75b9cc54566c1dad540035988700cf4516f297b8b5bd052b7d8db348b22386826bcd84457e21313057f208450b93804a1cef661037bb79096b4c3c4420ac586a8e758643268c68b10f66e3ff654e33c848a22097fac60b42ef99143d5064edc303f8afc8a02b80f6a451e1989053e6424ec8660888e752da425af366c732c260388cdc943065a961b2f4d1243906206646d0df7bbca4c84d5cd7286ee0632e1ef76a9e65267bc51178ad92f71e28ba613733fcaefb3005bff4c1382766ef78b56baaceebf2b5c0097510c88074d25212e7de4ea106dc9fb4478fa755f7dd7155e3ba1c11206472a015beffa8b18bddebee45306b7801af04231ff1d5de25a53b61181883f8673db89c23048a6776c63805efc467e31cd4c4f3c8d2184d30eda2ae1a064eace0006d8b13c66963ebd1f7ed64c93981d38a36c1855014629eb0b88995febe15856aaa2096b11813983523513c261d170192e576a3ec78e8a3296c1803e9f01e24742d99e53734693f2116c2ccbc58f572caa48584f06a335e2c627128cfff2e33121493236b3b9c331d2f82560d0800d498428a3199cfcaf1ad151a336afd730585ea0b60a208db198d0e580040b6039619309c88590da1e22f16b4cec1b378e37643f6f18d64e023616a58adc95c33035ff1709a982d8d28c7ac6b9162c9111a40d7eb0dc7d8c39a087c97a6d607f3caa7edc8b82b769997e21a0e20cc85ba589428f821aba4422846197e20f269d0a0912bc098b3018831197a70e3e510e8906450e9369f5509a9529c3228c73d2b06f8bdc38f899725681307f82bff0fa0f1361f93b43dedb32892ed335c342bb57c520d6c6d060dd399197bb5542097f873f245944e7bcace86cd86cf3ccc798583f18cc7119a099302c9a32c9f15bd7f32070a9da193d92a15a7ffd8865727d2cb37b52849bf84702c7102708757689f4a92935b0619c68216986925df826d7a397de6b86b37e117f4aa6a25ed862ab57f34fcd7a49b266ca724eb736bc050b54606b8711a22ab70a01134cd723534c7324a86f82e818f45a7507780366d2c40ba030c04482cfc1dd7c5e54cafa2a6d2ee04d2c20cf6d24d9c59163bebc21ba58e0ab479363e32a5b3be3e5ec59b4dadf0e02d9d8547a65c7862e33149d6a20d96289acf048e848a732d32dc038fb1f5679f54d95952fafc84b775088e2e2bc7c123696290a7a604bb02956c09a697a0c1623b15f874be73763622ea210c5b462ea4c8b80e913878992bd5b7fd9ac386c6e3f35bcbd13fb1a80d773244e7ea50004762fe67e7d899a98da708a473b367c79be95817e520b2652ffea329fa8a61d66c2a45b9dde648ebd9cf638fcd85132071fcb47a167c3740049c85b49532421573f6a79280a719ee9e187448babe9ac30c9eab9889609dacb58add92d702a363750d9a3ac3294de1963d56434eb026eb5bbb60a4e4d01e3443bec6dd02ae66dc209512264c811caaba55d47f55dc6710088094a6bd9dd450fb4b3a452992c1c570ecc693a8817d904d6b9a15cf7e2ee62d98475a8fa8ae17ac9f2dbfdbeaf494c7eedc7e9b7124aaf2604b83ce124a03d88500adcaacd69f533135270164837b579693edccf881822128041969212f9a4f4708adec033865c2421e343d3960fa471246d253e633eab10460a7285f30c0d3bb4d6fbc7765fb379a9ccf1ef0bbb460770d206627ac59369589b4117eaaad67070aa607409776c05073040e482f44cbb125ddb6185a4b5a5a25268a6d20ee754ae8234a180ab6b1e0d2dfafc17f15059af347baa464c568cc44f3578cb251f92a3a50c68437aceee434a1ed5accc22b86a8655cbccf2b32d77c92f251f607ccd520ec9f0da5b8286d0c1b967b587c05be80c17bb161f2f60653f5ef27bd667f5098080ec553850eef6c77be8368c489432c0e0c954c2367fe1021bf9d170339461d7307c61d37b79384413f398e247a956401b3534b821eeb574ae886a4e66a94156b0872bc2e0820c0ec43ab43ea0c786f5e5023323fcd46fc049e914834d1c0aaadf702ca544a07834dc8198c5e283bf99637b6ccaa77dde5adc8d96d178957c8536ef3e9fa4d3ca3bc69161000f2833334a4c2c905d0116d665b9355580aab51958d7de6c55acd4bc30cf28cd60737ff7b7a053d70227208c109bdc609a6c8b08afaa5184b5bdb40a4fba0ef962848162b5a2e0068a9e15a427f4ea2064c83991bf29766f5782ca7b4d19d55ddf981e4b729d5df50dd664eb660e146f2be0be476038120e2ea45ce0267a53f666e897ff54af84d921e1ede6a8581f19757344f0e0c085afab7aa1122470c69192614eeef42e3b8a35ddd0dca40c19143170d5fc008d76323af7930a91d51b0a961e355a55bf8122bec63ea0c051561d0f4d585759a1ce3e618f80878169ffd05411bdeb318ee5cb1811afec9da8ad57a349f982639eeab87774c6d3fbf170270bcdf20e44a1a2d7fc784061f9d7f252f7d3df8f64c91717d7be8f2e9911c1ce10d53de5e9b9e23801a68b674793505008b0cc0bc3453145c65fea0de59897ea5f93b7dff98043f80e7842c9e0ab6132d89fd862733040526e7dbc1658857bf248b97f9a03a3a788b9d43aeb76ac49508b331d4115a1e91dd230b8eca25fb5412d432dd292d1f35d81325dac07a3820621376bac23660307ad6e7101600e1ab14319c55071dd18647f608bc3697119fa619207d6d6a52e75a03a73f69d31acde0591b4068a0b8f092b9bb5637c136ac406342e2025439e09a69d0a94d8b4d8b0d9445805657b160f3f03b8b9a40663c6109a6f222ae546436b4410f624fe80176bb360f3ab808051aabbfb38ce675ac0ec91623951d393fea00455633d4a3dffb806a7e9805fa90fab6d61eb704bf7e6689ef3ba08a1dc9944a18750530238e7546814172a651d2c7a8ce4ddbbc11b3e306db0dd5a8bf8bbf319fa7ae7989703af41086912dafd411762fb9f6e8deaacb777810d64437f28284ae6622b5b518035da643aa481e5c7e6866a976cc16831ee6ad65c87d3d3c595531ff8b3c2545c4228b0091a07b158f675b8622f9647d073b90ba01db36668a452816e63d5077b342c5c5dcf4d4875074bdd96f8e513d6d7b85bca5e0b21212f1ecfe906fbbe83b52e032d6baaae60322c0988a7f2366529416edcc5abd2a3d8c2ceb1bf13ac6bdc49717d6ec28d93921a2359437564699142d87f3a88457b015fc491bf1647b38227a5d4e1427150e9311ed993444838d7d354c3d38ea8e525c18b09db2d4860a2a4064d94b8e46812a5e8e8b75a07b25ee363e56e82855edd322191c5d81caf72c4b103bac8e5fe139c55d84326608d2b5c2b6e3327250976efedaa8b4af3f479f1c7ef7bbb0b63f00ffb7178f29c0c4164b854cca55fff60bed7fb4dedf55315533667cd847a2577d1fd9fffbc60e76f1d125ed64f22358363068dd9fab4d3ea33b8f04da26a1206e505d48af645dc3b217abcd6f8f54961c6d7909fa3182cb14ef493a0840c90c9e05800d2f559c41b15435e44d85e41008b439d9165f14cfdc8036409a3723c9524421ae23969ae0bc619a188723808311ddc783ce8381f8a6b6a957b396cddeac23ad157797ba4c995e7570c93bf5ff160ab16ac25686072f2cee14d1a40a0cd870788dbd743e44d076730b10308104639459938622ac73e243fc6442eee0973d6bde04014963ec4180e9800248d7b9bc3d378040bcd90dc9e2f581fa0299281431037bd06e45fe0f094e744b314122bd8b63c74ed3def6b531884a6475c09cba3f84c27edaa883d47b1a8b3f0f8900fb4039ad8050c12a2f79d6a600df11ce34e4b8597c49c19b8d245d5570a3d89d1d98700819e7980d2771c4ed7c9d74d7c8512db42b557d82a9fd703297c85817edc3470a69e95ebf9e6be950e2669d9f01610807ca590496fd0370c761d5c3e9fa21a378dbe099e22f382e779a0e51fc089d75dc2f92ebe3735ad1b05d9843f160ae42a3548ae15f5eca457b0e659f90177f503cb81cc1e01466d6764ccf636cd181980a68faf99b2337dbf2a0c6d53d535b6dbbcbf8be549410bc41cbdad89381b64ee3e8f9ad8257a17391a9a476857e94b3029bd2d80062d4df106dfc6e2537f131176ac6ac305c686af0d97575fffdf0525eb5af4bd46f7a06289666ff6d02048ecd4c353167f1d2f767f0e739d1254c116e5f739520e6c133c53543658a3ca292ce8d521b0c37552f75e5c711a4d4bd000318244a69f1b8c8bc5ca5b67deaa9a6c0e14b1db2cd017c3e90665f54b6d9a8c03e7737126329ffb166ca31109f4e1e319ea16b657362c9950755d31d7084d2010b1ed007c9d3e3679b8088b2e11a868cdcee8e4fc721a559f1f6032a07a8165fd874604a3227821e0853d21f0104edd08a5eb180e4a20fe2985ced1ce5185c6d3c4ea0918af677868645d7915a4b0453e9c10bdf65cddae20be1bd9e1f94d95b666d901594025211f2a0acb063032dea0a04ecd50df10a945ce846a19e5c954b82952f70b78c9e0ba3a2d096a7dc9124a8dc1b738976b09c710ba65f505429e521d2bc813c865215782506ba51072321b1aeb02f083ceec455005e94439809c4c440540efea96261d8ee18028df768e51da208904ceb547fbda1f91b5cdb85295d5fe15877302fe6077fa477c29193ec397ebd9880dff58ec82c5a086c484c6eb72f7a9ba25e3b7b16324c1fb202a3ee4de1308417d79cb20ca1b4140a601dd246900e4c8c07c57808d2a8567b2ada679827c24099ce9085e0637de75503d7476e2964b2e2841e26c9fd35bdf1a5124b4d139e298fff7bc26d74e882c2fb3d203346f2bbc69a86c2a481cc0b1dc4da4d99ceb3adaf68af0b57c70fc2e4af2bb2e54c7c52633711c7d57a9f2ac4121ccf6e95827c963bc0ac54ca73c0fb1289412283ddd2b78916eedcd89771084f11c229064144cfbee5088f3195fd3c4e1610b2aa99812893228c3ce4a1e8d25d1726e5664481f8dfba85ad3985b67b03cb41f46306a46971553c1328cee96a4b395802be10346320214951e3f373c81e5147584803c0aae8b465f56a3a2a0580204fb9dc2fc02db5f1decd77b28e0c4fa269b8a7a01fc1e2c3a50d55ddb862502a4378a36271d4ed626a6506ae4582c8725b090e4cee8dbec1f19db2fe7852fbce2a917aaca6ebb30a2a22a10ba4f903c8135214f1d82734f5909500f7f52429f6d9f5052934aa08363ac2c7b5c5a2cb94b2cf43153e93f41ec61cefd3f38c832f5ed0a70d7d17f541d84f7de8fee5da71b1872e1209498f13812a3d086b970bb406f8e313f4fcba513875c43138ac24298ebb55e0d466277ebca71ed0bc8f0ab857021b8aef3233f0d3abdedba33e65ff2d822d9966cf3132f11056d4ad94f66281edacd79aa255448f7189db57bbc9eea28e235dc3197fd3635fe30f91347425dceb29f38a405a1d52342ee647e3ad9133d2caa5d71e70764569085933486b3c3efb20587a508f449c2fe9b012da6b2d02109fb4f063ac49b88c609534eef20803c3b7ecf4e484f28bf3b994b86effd1e8de8031e21f36012d3bab71af740f9d3b2639a910b10815ce767613c1e5bfb7484f53bf4fb1e3c00539bd8991a61848ab45b3bc104509598e4d1c13075d8fc8bf4091e195dc18d8102c2688ee833eeb2352ee98fa2a683f011ab7b3f4790f1c70a6f51cad06c4a7ede8007c9f90413c8189d2cc95c449bd45f78832f992851580f8303b53e53558d849001c64522dfbdabf06285feb955e96e1bbbe410a22d842b6780ee436f10b012876688d4d6d5edede6bd3dbaf608ff8a3e4f64df9126237d26bf41935ce30fd7354a904d9c215993d6600d3abcd618c48e04a16e6cc73f14a986c1760df2fc244f6e5d6c225464e36b0f40057c8d86fbf4ec7e5bdfbcae456eafc426785eba8269e58b80eb2952f989c3305065d93906542347783666b83130758040df5ebd6d4c4cdeb316521ff514cdb4cf8a983cfc44d76f402f58d66c9aff8e7e47b77eb986951402303b70e80972d0ae2abe3f29a8a3c5f9809bd133c1f397039dd5b731bec64d5c26605dbbd3f3f781b2fc9702baf2bbd6470ac796533b99bd5a7e9e69b2187a3d6abd8b015b6f620c1ffe7fa5ef9a609cbc3deac1b33d4a6c0e5622c37c75899b1693a997db7245e3f1b5c109728c1e3dc309fce937fef442b9c7184290fced4944dd382ace819f561bd30366358426c1a45b122703be504ae6edb046b68027beb6e57d42d086df6cce1948a862e5cf60107e0f390f1ad57eff9ccb468c0a91cb6572c57a5687e12dee6ef9db98b32f0f73b5fd9c2188eeb1395a6f150c2f31dd25caadf8b083d87d607eab873f2a58f0f4624e365db6d32a474a924774c70aa4b20fbbe6f1339f18c49a8b3f09cde00a838991cc2eb2600c4c1961f80da110a5cfcf4dd9895412d05b21b23604c59ff51386c32224be557ed556b2aea2bfbb538ef76427bd6279f0284dbab9af7386f16ff47be6002fa98cdb84a7561e8c9b770f4bbc01637f9226ca1aa2a1d697397ba5bd66f0a678f1be8d2a8f401778033806b6035cd292b4caec3e3fc43fb3846993d1e77cd3989e825b4f23651667c7504ba0c60644f2652a8db08019d162b178d028bea10c137b8b8ec4a6245a07a26518107e5ceb1a73c94ca3ba5a96c3d34f30c2656cc47e550f1012c2c86b363d74e0e82cd04d44628c4ccac717a4e6a2c05d462a0a3b79944425c92e7e2992ab58d7955e3930dc976a74992812553c1654ea2045a573a4124992236b18087c5832902ba094234efbebc50e9475d3f6c14722f06bf7aa3c8b363d31768ac73953705e6b4ff44713f147291b8a85acebbd84102a1ff619c2da2d2d526aa52fa1a41490dbe134a97fc113c078fd378491730727840e44022d22e715f5f3018580f5e54c9d0a6ec9e0f94fd42c03b1fb5a199a8124305e7656fa2f969c810c65068f3c76c8b369b746f1825b08f277fa779ad055bd5712ca870b906412bb8032dbebf76a9854e54880c7b95003acd43999c60f9cdc7ddb2e60dedb38f07230ec27590c730df33618ac0d28a4012188091cc43f87bdd9e8dfa21610d51012455e235a4760bf94c4d096599096b93f2e0d428c117a3d73306b6034825d084a4c3d1dd625f881d191c2ca327d7c4b4405924659cadb7dc18700d8647c0d3ac241c9b22c094c8d9be099cd56f8cffd42455b87ba6a3de492b4f0f320c90ba88270b08854aa8a9e16f67cc86ac475648a22bdbc6ee90031f512093bbae46c53e8de88d974912add9ee73aa619c100cb96aae6dda1947074b1bdf3eb392e67fbaa6d674729d24868f423b3c337df0468a92255310fbfcdc225f3fe826fe6c39f0c3570dc4800caccf2c42378031d2707545fce0df147bf1f577746bca5fa0704d3e814b2c49831ad5c744ffe904f07cb82ab7f2e0a722b4afb6a1a2bfbb3537e5c2a363520723e1011b73a16fd7782e1d1bcad90e3f874e2a08457d0bcec40a0777593c078293b90411b368670d637f530a3758eb0635b4703bd0eca6f3f08e6f709928610a6669775f188f85cf9749d824990951d5d7ebb86548eaaaeb3d8dfb6472a0a7063e876c9c9689798ecbe36622f0a5f7fca09a2295a5a1e4e6fd128d478a890dd536fee0fc448863bf9f54868b6935f3ddf0ac5d7f18e88756c623d6dffd12aa95205f2b5143f7975efa32a3a3b99bd78600fe9e28296b79c25afc2b49e439c270303a446239f34f1b39adb6c0500afeb5d86aae24d45a319f969fb8e4da2f032e76a1af6091c88e41a8eff3f3c0e1223128bff1c9f0b5ba54df4d66685096f201b3cff91a5295e6fba04f2783e60315d44a06021ef15d4b593a5b1eebe7245e6d34c4c87a35774f2212f00285273455f1cba8737ea894898b75a5d8831a7ccb987ea3e30306ff9632450bd5f95bfc3eaf30f2293043feebf169b9edd103332a85c6fc782d3f0f53a9cc0d0b7d48055458ddf36833bdc968b3fa071252d4087a38f06ab25ad305cf193581aba348a9e120c001f25ac6a463a943a0812923e8fa1852240d4104c27b87ceae373a1c39dc3bff25621297caddc06397148084e080262e04035b81102c40cddf0c00458a444a4f134e86da6b10c4afc372b57061d7dd93019e2a78871be61067941a49ac8f5b7be5a061c793188ce5958d2f3478a2d99f0f70308681fdd98f982835ef4ea54c880650e0f7b4659ce458f884678ee2cedd273565f499ea4675b37babc6a30b1b75018e7ba7b2947604ae878cb059ed6bf04cbe9a73b977587bf0094cf9a46db6a56429a917a361886d45a24e3f27d318d0fabe436623c122eb9e1f62d670e61162d24444e7b1ea2184dadfcae40fea67fc824359d726fae7e4e84484dc6b9848fb0b285111601499aa698da2699250127b176226a791e5980ade1d4007ef4aa5738763fb78d86a38f918943d867dbbe4de99017032900b75c2a64f5fe5523bb7fa5c52f56378b69c67b431e3db21298ce428737fcb31911c5011d154ae862f24eb32d4e23ba00c6c714b1270e8c293727a1ba9063483fcddf09a7dc8aa2cefd4dbd1ee22aac7e7e4822b54c7f0f522e9280aef3c0280f7235e646a2fb644a43070855ed70ccc2f356e89e26f2b332a4d9cebc2fdb57840354e33904a0ac25a47bfb0ed5e2fb7e571bbc2a3cf539acd4dc0a956ed3561c2994c1a53943e387c18eada9520c302caf967a30447250e1384ba1da52b11b70c45c58fa85f37101321c03ae325f43e5948dc815d37946b67d0e86e1f249a20870af0b8ebfde632bfc39f391c04dcafb10baddbf2221170990322b628ca84d1c293ff4b9c9b10b386a2d522e81f51d7d9cd0fb0b34ac35b4b87b7291562e77e7521f689e16d6b46e0df8918a8ffe21745f9a1d6423755c8398a591e9594e55a82839718d8a39f169dc19ae8149fdd852b6ea0c3d4b666f55296e11328661e76a1041c2989c0e6252fe5eeb0e03574f867f078d5e30760f2acce3df26c40b75e7525d0e86f82a25a637b1316245d02bdc7d01e6e4a18c81ce678c6bd1a4afcffe9f7aa001886b218e4b9e0fbc6c5e41a983a6ac4649bf3e15ec0dd539399875de9106af0984566ab780a9b674e56ce41c747893aa127e01e4da7301c56dd6fe284a1b7652c49230a6b2d2c5e4d12d06df661e9090bdb07b9d492c338b0f0b45c9a02c32cbe7c44d9a464ccc8625032e2de989163d1e292c6d0c3c49f71aa1def0aaac15219d43001eb3faab9eff34c21cc0fd6367584631fb6073c8d4e3ac6eba91a601f9273cabc7b8f6a922197b8b2596cf5d41daa736c2bf5f0830ce7027ae050f0d48252230887f0a5d9cd3837a2bf8cb042b101e8837cb098ea289a8b8c0a96bdbaf3e4621c1e44d43498bde1cfde77002cea36ae67035ea139e1fb7cf324c323d0a0c5ac7c9cac54cdc34c89644cef2c34d1a685f6949bcf3b11847dbd8403ff935f0f41a2f7119305cbe37342f3a1c32e92b18fe1f41c8b095615f6c12c5ab535001514fd6357fef81bb236f1430a07445b9dc19f0d0b4da4a90acae6644daeec81e189d3bd407edf5fe5b037e46bfb8e5a36d110a2bff2100901f5bbc7b2f7041cfd809803b006bba7fc5527ced5d640419135f285b2b460be986c702b99cd2a7838f41465cc08181044222b5e44d1e22ff94c0790407bb7ea3fa7204cf522a7214706629f38a6da3ce99135a5b56ee32146098662f480de750974c94e7d5ad9c290bf876a286757f8492a5da9122a1c11430a58eac8169c4d39040f45cb32b2a9d806605ac564d8c9bc7afc7e51681a3107d774789939d66ce254413cdc700338505595b8034ee72d59c29020032d601f38eda2b0bf1547498b9595cca8e8dad09ccf9cc576268dd3d88a11f2d4553750912637754d6bf7891fc126aac068262ae20863defe9f59a430a2d79d39d9cd6c9696be8524898e6bda0c49a8ad60ddb709a3926209186693dc0ef0e2f94ec8d144a323ec704cb4e1a69ac5f781c9aafdf318a17d2f4ed1acf7499e6e62b6ff28f70c3ec1d0ea63f914be60365065a813f30fe2b5ac8c9432be30f722c9c47ec6a0afd755aef4f85f7d3a6275a6c49db304fb730676e51fe458f677a6e6718706c225b34018afd1ac878e0f4128a3280de22961af4e53308b6fcb1435db2df3e57a56bf1452fceed46e1e58f9951770814bcb79eadf3623bcc8050759ef922a9e491b2aa0faf9dcf1b8ec02cf8d78fd2e3870776551bd89d8bb1493cfc74eed3ffb70f3345e82372427a54901119e4942e436c78170f02482aba7d16c85ca7c29ee76d4d82b622656330d6451a45f8daa12f1f19042140074ae480f15c7b4ec2247df0ebc3414c2d749b0df40751c5c59674d5449ac435165a39bfc144aab64f9723dcf1b73c6425d93d31c5fe8def18f6b4d93e5292ab12938f5b5ae80fb054e62675ca45f69378621b6b889383fc9855e234cf81d3990b85c07e09e7f5567c81ceece66fb39a68ab86e30900d7fc4c8de87d133a3e2f09c9bf342e302dcc26d90642f11474ae380cc4f4420f1216726de3b1a2fa5b814e41621e79f61594d5b970769b8169e825ffd33f338ee1c0755c8f1aa88723aeaa14f07c9bc484cfae924e0976a4fbd0e0fa31ac248cd24f6e4bf0d903dc72e6d435971f3f348a9d79ade158c17eaaf626f946be9bcee8fd30c263ecd4be5cbd5767f87abd32b9dc8e3f1a2d2766a0c54566e60108c3cf4f3294671640f1679986b4be3680e101b4c01a9803f4796233986f3fbcb459720b166cc6a08a71615176481499b231f65864a55a4d3d7a6c332134b8a13bde01d31cec9828da817823b8e6e0791c7d5abf7ab1f08bd596ddc3d9151e094b7797e1cbbe1fe4b195ae957fa83586ef3e93a5a8442075a25aa8e1d04bebd80615c5fdfca99ac9efd0b08db9ac92c0c00a8c811d6f5446546309fc7f6f01099ce24a341cc8e2ab3312ecfdfe9bb3c95d924288cb130d3f7ee0473c4c47a2b43b6aec625e78615ea6e817d41a91d65b54b4c0070ab312ec326a838c2f99fe93562c641d09475e7039f4a807995bbe8b129b5f4acb340ee74d4c6a38b944ac8234bfb67fef3985db1ac967c02530b0740d654bb47a6c316193aa8c0117420da4e6c9a82366d982c729cfe450cedb6831490ced7066b27ff53595feccdacac6bff1c45a59c463eed08938797083a62ff58eb80c4a5b9437003a3ca039151e7ac7294cb2c8b0a9bbb42a82fab7dd892cf65f91dbb9c32ce3a4c0f810f6c768fd55829b7069fbdc95abb75036ad8f33faf29b50133a688caeada9d5d3b3a2fe012ad898ec222dfb73a6a504c3d54880fc934eb668c1c5c4f1171ddcb1eb039833c3788077a116a1550a3c7df7b0624509df4f62b008f42716134b58e432007717cd7ce7369a4db881aa348ad5a1322213226935a3e89e5ae7d20f38e2ccf108b1485a84f4822646990b206eea1dbbe711c8e5edd3fd3b0761a4c8762cbb5f6c20e03a5af6e006698845bb7153a23e8394d17b695e6c91be652721db59ba1ab0b9aa79872016f0250815823f6db195161756e62d8f050284a71a04f7f15413c8062407b2813bece3214819e2bd1d72f500ad3e35801a402e31affa1afaa3ec78b18ea6ceb6db6b8c6aad8116c79d0d271bf9ba215e43a13ddce12123d7da434f236783c11ca95d9474d59947ad6a1dcdb2d911a442e5ca35f9c7e9a49528a5fb86524940f79194f3f769bda78019de284f7570f1a89a0e98866820834b00925ee55006109ac7fa2cb9149211b922b5e21fbbd723a2e0de1f641ed401b01bf1f3bf272c147448605d05205ae4e1ce0c998283a4e7892968a54e17580da67e2490631b75a6c7c944a5f1816bcdfdee1fd5491826edd020675a2ecd867f29f5af04330b5abac3cb36d7ac2b3742650570829fae19f642b11705142400be19e3ed85d13ce3d84f84b4ae6dafbbb959eb0e8fc6027b095849159555dcb7c3fe622ee66a977785c8026811ca378a3439864682400847231bcd2ba361c3d056d89833a4151c0b5e06a4559b9f345710b6e80eb4c2dcca5e5acfad111a0020b549a32698d7a98229cea96516ff0460a35f5bd7318c39489ddf04437c3da4502ac3c267a1ce8443de6ed2773e28d512c77b4aaccf0d81c3395c0faa70f4d2393b33b5ec4ce755defa353996850442536e969c50ed4a1fb624a3263c4712142ce426526e6f961f202dff88d608cb1db9aadd757430aa7b8fa6ee1b38f50615b61142340f229f942642b0c290a766e15787cc52601436ab2b1c46db7b5613c7687417a2d02f95490f34c400b9232a2be9cdc82d7175013838c316924b0477fdabdf8b8317cdfe879ec5744fc2bfe01d1794ca04880cd884ebc7d8bd4da5efdca46086f5938d1672f8a965d696490214f625f08f7602997c6b31c1100c11db0f06520525d33b39a0795beea0a5a997648c08ecffc3458657c6c152871f6494bc2e8e207737b02c15a6e600ed31e344fcc142a38c284e616e7b4573903766c2db281e101980b5ccf693a6488102db6fed34c768902659754945069cdd3f817ad25c90216f602a23a788d7b2c8a60369f4fe6c8b0e4192b75768b910099dbdb21a64b441d559a4912c0456e2071fc6eed0862a71826b15f9de5cd38275e3f88a2afebbf18be605bc0b804a3d88187a56099c7c7a932d2106e63b664fdcb603b20b6603c2f5c4c0caaa4f31ff87c346eb149bcbaff06bb9a66939a5dea02f18aa9e35d8308d9bcea8b24054fc621618230b2a066b0673614e60024f813124cc10220712deedd76c102552e98924513d1acdffd11a27230342581c86b16b874ddecca9451e09da6fc10c82d1daddcc1937cb86d1a200a5856ebf796e209ef0c4ede08dac8f8fc52a1ced7afa853843753aac5d45ab2a94fb265f2ad5f0d0dc1ff452b51b7f9d41e8405c20900e299b5115ca84236552119c860a047706c3daf6d88a185db8565ddbf2a8f4b9c5dae30970d413dfcb8eb56da800c9325a1a4dac0d9c6204a55d1f8c1cd285d30bca7f62e4d796b281d2703d98a080efd2950ef483490778b6361c3189d101cba264509ee456e5f2008255977fe506181f57cdd565a277236b3161c30d3b3ce5239fbd2bb201d1a4b51e8a285dab55e11284c192e66640a0dbdbb6d0e386401485eb7f5402acfa352d1e189650c44d4163e0de741b9ce0fe3e50929208fb8cb1c8ad6ac9c84eaadbe4fc85f336b680d8b4672510db360d897ec12dd289339f098bf2c185f4a4a27d16a711e16f27c577dc05539fd6ed85e1e774d8a3c4dbfea4547c83479056520b2dc24dedf643b3195183e00c122029cf09268bf9453d5cdf527e71893d32884adccbce957bcd9640bdf86d715cdd5e74cea529e83787730ea6a6dabccc75e6309a57049ccae63f725ec57161d262ad01b1fbb408db6b4a9189b0ed64c374c2953e5dadab14dc256df146fee23aa1c8c818833f018950f5103d2493a731e642b2e7cf9157c4e62da460448df35f0f26afd61947b182a42882c8aa4c400203426302b943a74e776c3a8c5fcc0357a94608a97a835b762775b5f67ded67cb1234562d20dce64b7904026739b91a64655848c3e8ac34876fa10278b79189fd668a58cba7b804a911b011be831ac3eccefb144987a1e609f75c27c2f83b4c3ed7835921150abc885a5079c7b16bc0641111832dbaa09e1484106a93b1e1b81d58daea37928016de792fe20f7a4c7b3c9a8474cdcb2c63592e20e1b3df3cefb7a5543b85c294de1520d920d795d6bd444ff6509c2f2bcb7103567136688992eb39861956ca1e033d18ed0a0d978c760fccf722098a8bfa9e8d3186123f8e41dffb4cf700c33ac53f954369571abda491f2985dab1c704b5a6ada56723832e0348b71006aa136af004e22e5140d1035517a05d1c4e89696d18980cc061cd2cd52b400dd1c1acf23f197ec354e26c3b8e1c664fed7481b35e827e93f8014047414b2c39a58a7abe2586b5c0efaf77b292066078278057175a448ec9c04ba7baa9ec3593879eda05f8802ef6ffaceba6d086d219c039fd39147483639d39481ec6178adb81a17140e21ace6cdc675fa1148af936fae852a32abb4548e47f2c5a65b3e680a86f527ddf43cd6534620f6a01ae9c86622ce469e98430a036b6eae9b5e9b21ec81c870789b7f5f038e7a5b795c5a17a705443f1404125a5e9b5f787aacc7ac5983f9ef3cb6f7306e51b6125b28eb034a4dcda79025d18c5363d9ac7f6421a8c086a1e3842038689e8456b056fca74131836455a1f1062927d1cd7d4eb8314ecaaec559f6a938f5a49573d6e8f8df130fbbd2bc76537e9cc0b5893ee01f3a4fa0651ae6780439c23911ecf7988d9dfe9e11ed57067a80a95a67de7beb1d863ca2c545392aec0f280fde9994bc19da953911fd351c1ec62b6469bc27897e371a493d45ad343c0b26db47d91f11ada2e83f43b6a066ebd09afc973a53af8e97b65658d5a903a48b6eb66190630b5ccbfba0653ebb8e442e253a70b37373c56d0cdf60e1e34005a27a16511711cde786062552ac4c8c9e0746211275f652f01ad0d49aea33708db1579d31f2208c34c4aa62b4d4c87ec53080ed41b0bbef2a2aa0013184a6c76bdc48a4d771cad0f6eee948acbcc66c900fbd45c239125c241128068cc49e47534c5e2fc13c9c8f5014b5ec05c8287cc8c96820921648c8389f49fafcfb3feb7eea30cb336ac8cde74746a32370b322daaa9648f3bcd5b71193647471580928ee9f4aef623d767d415a9e04e72832dbd5e1dc554c3c3edf2e357a5b870427df5937cc8c40d5870779d57af6010573773119af6ce3fbaf9f584deb56e0ccd1b032f81197f3e2b2074ff004152a9cbf5d48f55f8af4431f1771c61221e60f01efaa06284b8f48c19eda3b66d872fc61c20b8c50ad5e869bd19a724c46db0b9ff87b59f326b91689498b0439a9583eace2c65d15533a8b857e4b281c8ddaa999cc8309cb573bf830fe86def6328378b0853cb60400b94f66ccaac5db9c18b9dfa0af7da3c711e3dd27f91981b4aad7ec6df71d472a5adddd80b8ad816703d95e7a6bc56800772110a18ada37d320db75163ebc80bdcda7c541ad55c4140b33061ab72861654ddba1614adae42216869a6a8adc73c6b4d162a823b1c8ed818520d30a010da0bcf1e518a56ab1c7ad96a497d5d556aa064614fd5c2fe3bc568978753d33ff9184a39533d099c0a5bb76846692e2d3a7453a7d203df2a1e648dd63f64ec37e78a8b072cf80e742c5bfe6577ed941c6e1782ad5f52e1a2952bd98f0277d4b9161e8029eafba4563630332adaf2bec51bd43948df7c0e17796901130f6435d618a6c92c73dd17b032c0ef475c6eee063f5756b7346befe166124a524d4c7a9269dd5a8cdca8f26b1f595059d7f0dbdf728181b6c493f8a14ec97e487cea1ee6cd4323780767160893840f189051a60b51dec87e1690e44523acc2dcd4b7008f386d04929061d7591448327177d5807686af77a83f0131272d69a619efe823f18eec0d864fde2a08f13ec0900a82e20ce869ef06f1a60835aaf9cb225c9c40bc26cb375b88016d4c367b289adf648f64f6e4c909cebd16cbf66bb5c2ec87f76026c87539abd6dbac47a6c4054a39b4289ebdf1f8b818502d13b3eeefe075be0ccd01d2a96b186db80d79358c754b14ed1665f729607e6a429ea3e90d32289d2037a820e3fd10bf42838f551253d99f06e267dfa3210b379e74381128116fc8684b6fb9a0f5944c502a684147dfbe74f83407cd247e337ba131141095e54685a442f3a27ac67bcc32a79d87024b9bd6c739b464ca4e1b985fd088208798eb04bd852eaea0e3ae0c562191c1b86a2c386987b448105b498e64ada3903716836f17918509f24fa25d1f11674e0d4f53a391111058fe3041b958cc4a373c82b95e18a41ea9a7db0447c8bc12b917f575cf1aefedc5dc5207755cb76f56ced2a80da0e38bbc2c301c50855d8153fc4f145420eed62c7ca3366b142ba25e83e7f83083ac49bed7bb31db995b8171dc43a65f09020dede520bd5a7cd46738a8ae9ea4a39dbef6e7fa18d1a9f8a06d6eb1f613d290ae095f48fdb0cbb3127d8b9786dadcc0afcdf1f7912d7b40c36f4737c224b8dfd5f3633915975c0215b93be86a8493996109af3fa0ad71a1489c3c11aba2e86e240887c2019a259526c243f57e18d0d54ffe9c7e84fded5510b1e46b68df2e4f6680f16da790f9bc3f0bc13635e2e9c0c41cd71645e05d19bf0dba49079d7523563440be52fbe432354360f1a1d9085852dce8c67bff2d565f24f0a91d57b5d0fcbe40aa9398e78697217cb45f6963ded43d6086f621967ce6ee8c4659ca07801be648da271749027c332700c0e2560266f4ad94778041028247a8a11a0cb84e1e7f29a3153322b33efcffdf5573f8d7732d6c803c352318b3cf4d5dc8f1b716c4cfd23561a52fa15e26f0afe85407d105bf94926a3113ac997cbd266bf19c15d72a6b3418a0ade70e024d161871e04970fd7715d550607e35ecb6360eef5f597aeace6ef2508cdb9d7488d9a9e5e696aac1bc120773a8c6358a2e1d3459c6433042f7e5028acdcb89129eadc1581d3a2e2d3f84b154d5a3ee469f5dff2a6d158bd2143e55a61d0db50b1c870de5c3c34b19c3a380e28258fb48a08ccb458250e56b8d4897a014289ee36aeeea11eaaae6e9c2644d5050e86fac327412e6183cb45b904c4133b35c1273b35f52393ce28f83f3f522db290ba1b0f6f79d9ca8f5a4091dbc2e8c7e25be09a0be13b3e5d28dc3053fa2a8230b0f04fd155c106816b35ae2185014bcc10e0a990427f96bf1fe0477c6c47c481cb20e5e147ec7fab3c58e321aa8932b50df59fd2d0baae5400c9b527abe088fc55bf76eb4575ac4596c52f400359e65ded269f961b0442b1ab0c2ecce8d9ad079891cc0466c79ccc3c3a323592380cfcabe6de77868414d846e59aa99ea72500a8bee4d7e2249c06488beb8529983ccfa4b0f8d14a793cfc4dae4160397ec33fee08432d77f3d0e3f79717c17844f97047f5ab7cc2759d39753c818de64bc021464ae47a251ddf25430edd1cd9d2c33f3b56f814a8c34b13203743762b47cab28913096042cc25aac55ccd2d72ad3112163a026551e35c9130b5668d8be0f4b7b9aeff97706244746374c3bfa2e1add88ebfc1b8796a16b3b307e394d5d4e759bf1a80d30358c6095979bbb784ff6fafede4a02b03e373456bb16df0dc10406d86362c2525ef6c34fb0901a37c70a3b861b899ab495119d0269ecb04022824cf4f810a750b632032f9de5b02b35fd8c7a4372e1acf0e874f12a7d06c998415c1f15c39dd774e338df5185caff419977af6633188e26a92a01beb729ac90e8f010bed0a1fb65f45eb3d4ba264eca084501d9f41e401a63e51ccbaff324851b8d0a92c9285d7aff3e9775edee00325210e5214b8d4c02f4af13e50021210d6ace834200b4715405b2cf28bc95f495cfcdbb9566077134ea896fbba6a8c3000a59d7b830615b4a2ff9ef8f19bf59dd230c52184b52f466207111af8b2f87913fed05d11c5e19b28de3dd1aab06e13bd529122d0f14d68d5ce86dbada314b107b9bad8c2493c99ce3cd57e053094613f0e02b9ae4772612417e5e3fe08ebd32d089c019ddde7db78ae339014604e302fe742706eea5d83c92ce24b713893e7d91151a49d84f82e0066eb446ca003f5deec166eff1de11fc5cb8c25b2aa03e39b119444aaccfcd6be4191cf5bcafeb7075e863ac28f07c1e61aa4a7e3a0ba9787e9b7c1320507dae6581082e0357838953a9d9cba224b9eff5872a409ade803322e1943ce745f684afd858f5157833afdce8ce7038111ed46dd2a5c1d53f8c26e4010cd709d1ad18eba2c34bcdc6d065a77c1f2469f5977fbd3d3ae36aadfc51723be6cfa9a8f20e1231e43e6c81b843f61af73d6852d2f4d02bc4e9f0879db65470daba140cea661e432ef73fc21f5f4b071b6d2b1d36a03299a45e734a04c440c711be7c7dd6f7df4390a34026e9e2941641505ddb8544259b626dc90938759e969400e43298236ece48a955fb9cbfcbf3bade3473d154706cb331449576909252859c1071d965602823093d0ef83cbabb579c231d33a34bce7e92d94ef263889cd6cb69f9521c216b236d5184902533bfb39fcfdc2c64ec420ae07314be27bd6caa088aba191ba76dc96d8772e59b9cd8159b0b003dd152240182ce29b1c4cfaca71b3fc0391adf8a5185a92afb54c90699c413e5cc92a10b4ce11d3e6ea956fa1785dd7d5bd4d7728d0367787cecd6d0a9ecf1c79397652ba19e2baae4b131e98a037707e2faac581050231920964c6db98ca2279a5b14bd962ccce2d90614ee5d75b99076abb1b3a417716f9a9ceab28d31adddc2508d825823cee80c943040b8c1ecae6e95a6758a702f0182803a8614feec4ec2546792af032aabb222de65dfd83726097c392762aade4e6b64db9922f81a402eae0c4f8b59e987e4d1d045e3dac6f4d3241d7f02d8be3b4824f5552db334c1bc9ae54d8ea11293fdbb3eee5b6f63f9e33fe204a9a3ddd31772b8363e1397376bcd485620cbf09b0350d677fdb90c979c06c580c2ba2f4ea1f129b6953e1c4c5f3a282c12b4384a44ff071d99f9d7fdecc43e0961e7fe99a42fe31b330242ab502697bcad832e0e0775b2e50e956a8bcffacc599072f65b0e1f5d289908058c27040dc5af3bed701828f4aecb1091d0d0a662278fc0901182251d53c308476f2928c691d46d96d96c04fdb25e58387f51a2f5328e3690d80f6bb8215f5c7e74d875aaeca112e98bfb315cd5411c4cbf5d268b87dffdd719d93395602952de4067e2bf1ad63b17da8b77fbc41d6db21b80a39f91454fbaad73a073cbf1a596bf9d006cb312951115733909b208b6ef5dae64d45cac2d14374e86d56025a595140c8b229366811f66394882cc15cc220536caf306e19a23c2aa16726581f1023cd4b42c498a140e07717bda289e6c0c0aa98fc930ec983cb11f795248054107a1c4fa5b9f2e00c4d8fb3e3483a970bb39480792c0159baef24862be36efcfaf0c8ab49a98c1457f216a906e309dd2d069af524345c46241b7910a57b5832ca1b0f12185bc8515a800426ada554c9725723a8ed5d4db2311cc6d91c96516d208aa373ddd342aa09287fbdacf6db91cee6a256d602e8efde60718889dd4bc1fa391c7d10b61a9b669ec11ae3ce04edaa340d1362f7408b11d80e96a2aa1e3997e46ca0f6b80c2303e5277f9127065791f41e1608181c196152e8654a5925f20dc29db2888d44542f3d9224d2928d08558bcb64e5709111bdc8a90eb21b40bac841a6ecf4bab81deb800308348dcb0f7759014a8cbfdc2496d48008fc424cd53bd5c8bb12f00426d465efcc27017ff01117df933c307902c9b1f78ac7133cbfc7c90ce9d1091d866a23ef1c293f920e49527c054337e2e208528e01e728ca7e961c687fd0125585d2e73d4d2e5d7df3e2cff1ac45cd181e9d3c610bd5827df2933d396e8bc2699fe62a47df4b7b8d12250b1ce7fe9e18f73d271049457ade970ed5cc104cc57265f9ba434adb9a643c83b64fb8f972302312dd8745e27ddf13882c440e4f371a5a58d40521b3185e6f485017f08c9fbfe604300b98f65b91228614abaecba29946dc6457feb041529d534f673b204cc23b928d35d029deca04ced28f2a559ca7b17e35d4692baefc34c6a2e7450b25235f2ffac9aa0d9a22fc2b182431432129ff1b3a7e23050b3348e918802ac38cca7eb31db7707fc7123178fdf80a4e4a2470d26552120ebc1ba9d52639d0c0e199fc09153a85aa96dcc3da16d548c2485653793e6f0125448c48dc35bc76900583dcc12296a0f58fa2dc4c0905223011dd11bf62d1929604438a50a53b50ea7b6878cfbb4904d34584fd378e23156008079cececcba84ed657cd5eea3cff2898a0e69e69b8702f51c34faa1d97b9b4aa0bd7c636c4ce588dd62c9058ae58e63123a8e15674851e544530feef100d728dd92482a50128e893e8de94aa0741cf2f6c72f160ee97fddf9eee90103881ef6d452c9fdd371ac0de3c94a80762f6a48366be712a18167b548cd9c36ef02e8da62b5199b57618831c777b6e146ad30b88f05511a35c5eafc6991bbe70dd8f823acf858c6495c359908e0ef0297c9eabda5240028a5580dac2e10c0b866dcfa99d9c2a01d1083a6233c89b3972f3e6716b3d1dc8e05108129d15f96acc17d80a3f2db88f02393e45fa1469ea992cc18af2ab43acb03a9ef935e02cc8619586b5a61f3fceb0d65301a088ab351b5086b69a39b38c9a5f6cdf5b197df13bdbda154a38289bf7511ac7aa7938b659c04064e4f62b3cf0a18b4962f355f2c30ae04ecab0e883103f929eb7ad12c5c6accf38ad87e379e8c6bb267182ce99cb325c981906915edaa431a30d3f04239837da4d478f147e168efcd7567a10c19b65d99b3c0791bedb13c06bf503427f6cad44c6acbf1dbcf0f3f7ad2f0bf73c29e3f554c2c6c0d08339458b24ef50a50a75bbec178c7b122099c66c76da55f7bd385e0a9dbfdd5db8888c00f5e495c85bb7224477a30b529be5ea22315c87bb08dc4ba9e5aa6022775fad43c614ca8c9f221b132c48f0c0cb09b88aaf2a37e823c83b0763fb195dd33f8a3e882abf2ce06ffb7e0127dfef582cce50f8360c385ca924bfbdae1d37286c66616bc52a68d04d9255c5c581ec04d3a79d33d63ca75f834c065a08eab5c8b1306bab19ee1880947350fde58a8c6b1679b97f52d2010996abcfcf5c603e627936f15663113962829c71de5e981e2ed24c27d638e841c20bf50b505ae1ce6a143c264c6ad1c1ac0472e3a0628d90434210fe4e411013d02863c5d7b6ee606b56de43c817073d47cb7bad6fe623b027b380b449762a63bbc0572120720a7088f3e6d49e6f1fdfd3a7f3f8acebb07acd3dd8a7638720d9cc9e9cb6a5d6cec078f21452c31db6dcc89b9cc50a0e0c2f04f7e06438f96d92cd814a3e26c51e2cb3360cd1de21f366052350b26179e7c9c3760e56d4c36bd0d08e0a8bf68ddd3b0d810540ad4e01d06a0fbf08f9094edfd555806d24eeeb2445a45712b74da867b7cac839d5734b41e535dce74dd65b340fa12051af36a80d6649e6760577b8460eeb42d9ab0d6c1664da18c2f6950f4eeeee272a459e42e03815c8997ba9c44891a2adc01ce0d4c5d8781582d0abb4068f53b22a3d7db28ecd97db37cb3f65590b1772af6cc9bbc72893b7d1ee3a94eb2930ecb113cf02ec0ee2b92d85f244d9b31a8831a6c2316c60bd9aefbf168b121c3caa94aa3d94f4417137a16afa7faa85c522d34903eca1ecdc503f7acca8283b0a6b8c8a44d06dca87ef840ebbc4123294aa6247e8ff64051f27849d02856c5c5493e5a01213159d4040092e12bc5dee94c0f123b84838b8d4cce605a071abfd5918395bf426f100683b53b59cfb2d4cb86e722864a27130084e72f21a621877ca148c8dbb07d3bc3cc7e2fdd1c76a9585aac21350d127a13b344a771bf19e4cfb08d7d0adc1d9829375bb6e6373eb26d1af92be8832c49a1c83a2977fd1b85114b0892a826342122834645f258b13454e737286c688dc18a710f18c12210ccf05e012888ec00f76a6d5ac3424e328105cfbdf5ab2afceaab1f4824efd148dcb58026f7bfad7b818234f960fcfba985abfb0e2db7deab62b6576c718aa41cb03cab2968dc91b2449022b16c16e1f36465bbce9b248613134dd1e67de1dc2d88360a5a3790c247e63e801d4a2a7f7a241bb332005e90eeef3d027d85864ee15eecfdcf0f7b107befd584711c4825f2dc4ada607f46b0551669dd850855ec224127138a12e131ab93703829833ca3d93d60b3e188aede330ab6dcd7a024378002b3f537eda5184b4bb25076b66d5aaf594c520e2e183cdc8ee377781ed410d73a783ba98ee462dd9a652d44913594f781d0bc68d49825067e1142ec09c814178829267a07befe77950753410433ac311035675c49018d4aa64b45bf777f6f62d7202ce792ec3e81acd63dc14759e67cb9adc59979e6c1dd1c818a5bbb1aa5c598288e069849b140b27d27182585d9ee6dd350c4500bb4cf890b58f9bfef37aaf3778f217fc5b644983398d3f7ffd43bbceebd3915dc9cfb8720a304207471c7b0030d117ea0302a2df6170460661cdccc72794bd72e8472db9cbd597dc5826703921dbe5dd4f689c207e2137538adc719841ce21f9e0321304bf6dea11619b416c3870a0985d2e5ac356ca333a57efa1d8582e5abfff2807caf5dd562e2107d15d1dfd263dd538ac5431a9340de46870ca01f3dcb2e9c043091724f93171856ee2f75c17b721b5761da1c3ee1aa672d9a7289fa7596a72b70724c320060e5b71beefa6cdc76a9ac10d31dcda1621c9f226511cff4df3d63f61353d74b858ee675e7b98fcb72611bb35fa1f1f1703dd536daff38643b2bc1428d9345db9466e5806cc4a6b4f4182aa29109926fcc9b2d1297927c02b342852415c487c6c9880d4180d2aed04c879275e4d4c713544ba40b3fa87b55573d929a12ab77b05cc17404d939fe540a8130bde1c6fb41f585bbd9cff51df46ee66250591140287240e8d38d90ff4fc3ef1fd95a68ba72a7060e89f8373b97793cfe9d53df9c7e3090fb45e5a5ea6115e40dee700646287ae20146a87fb41a3ee8b0e62bc3d6b331dbcc967ad345f1a07cefe5878379c221c11e21f67a6d650f8ba1a99299dd0e12f73a514e9d9918f9feb905cbb204faed689fe9d9f1c6dac1e91af931810e4641209b35daa460b03e5c4b63b9af3cff40263cb5b6050cfd9d4d82e9e5cd103d19fbac85de75eed398223a73a2cdba173ca1997a397c43b0a0ac4e605fc60565d2fc78a8954208d9a22582bec279c3af916828e2ddd6441fd1707c387d6026a1d3d188ef2643c3a29e68ca04c0dcecb1189348e2679c7d9b9098868b076b2290e6ff451c575480b8c16ad2b9cf2727fa8969f7c497fd249aeaa81a4851ff87b0f4fe82633823ed69aa0f4aea506d869d0897ced1a38a222999508bcd8f15505e06340619c72f0d898070b2d948a284768642951a5fa1d733982b8f7cc102ffc39c20398b7cd2880dc53e90c3b1096aba5bacce28b763408f2b18304794cda8d4a703449ffb3fafd48a42faf9f69ea7ebec9403f083332fec20184374e95d766b810e6edc229f5da965123838d0b93b9a82ed4e2659ba7b4be71fa919ffee6f61b298f7da2058bedca854410cc869289896d6e018c6d5ae41a4e850a9ea016ebf589213e3daf07876b89cf5dd370d23b90ca5d3ec717760d39641297f339781f25213d375aa432bf9acf7177d499730d93b993adc97c8f4fc462780dc0c65199c7a18b47536398ce14b47d1103ea28f8a897ff1b736009050222a2d5d4bcf7192e35ae7947000085bd33637ec2503061e09b3634437de1d822c63db7af7323bde46df43b808ff79874a5cadb80e82ffcd23ff1c76ab2aea15149f097d7912f84f485d10618a4afae7a1ba13d33761f40358434a97d04a301805d9092c51f7c2c703c2b4050f564058018e5b489803c8e0bd6d75b46b3bbe287aca3b3606cd9dae24511c90af8031d30eb3a70a20bf70573d37c5519a28a419524c7846a6d0a7ed2b4029a843ff357c776f7a5a8157041ad7015a21d68a1a1e11bc42aa37523a7301143f8cb2f507b8116cb11e3a85298cdcee4a8bd803db3a3c3d6d9dc9f070cfb3346cbefc7546445c3656a8877971dcb72e534dd94292d85af69d4312b9a318909f6a930cf57935d780b9a5037db0299edd1247d7cbc1580872a424897e482d04636b0893122f45422994e4015a2f994303e82b0980b50f6c923892f665483b9af4f905e95014dc9319dada397cf473b166a030e128de7766de7c1f40d93ac347f9456514c030c02d002de987ccb042b2d509e03a3faa5a2f129251a9f355bd00aece6b456053887ec0376d732e97b40cb361b4d94838a0738090fd20b85990aff1f5d4df4900114c0e1e43e40055f0ff40d4049f1dd15be3b0be0cc026c6d2e2e70c5b08b56437a606f91dd1dadc5fb9cd822bc754415e8bef8a9011da65020cdaa2f0b8244795a50c0c5550f31a9b1225cf78b2759746de6e4effb5719d843af419fdda4dfd10d43449b3a80336ce552b812021de4cfba6b8690e693b889f82a7cd3388c83082acd51d44a3cf70f69152bcd8b0aeb8817cc10c698d2ca74a2b599e144058a4d5475a709c67744addd1d29ea412222678d577fd4b859ed6201bcfb9d10570e346fc175ee094f038b85fc0ac29fd4ef4cf1d8b5ecef8abdf29cecf08b371cf91d581bc40c0d32c7c7d76b231693763942620f12a91222e08fe6870c1e2c79bc3067c74e7b35a0fdf96604f0fb5152c608cae26371a6f2ff066a733dd626ae1b420026222c3a22cfc49754d413d96d1a51b1469aa2e23081a5e9bd116d94803e5b03eaaa0ec843840f5fda33592723eadfb1fc89cba827f63ec834d6f940ab7c458e0f3ddf71e529427b2aa4060e3806302c60aa638cab9b078a0e5f000e3c5ff9cd7f1894b56528b8f3d9c59935ba22a9bfa2011346da1c313dd56c846d62ba11ed35dd376dd9f4dea44428627c650e4bc7acd49ce4960d719954592c215c5f91947c85ce9a0f0dca196c9adc8d9111ccc1de1845a7340a0d4a9d341374a5c170d2e6c221c349a9d66ac54cd923812566431f9e8152c2edd0d63bb6c7336222010c7e40737fc34b49b945f7cd9d1493c273ae96b07e2d164f94fa4e458423afe2ff3f82bc1a21d6310d4dc51f7ea91d412bd941ca42d493c8036735da9841286937910f9cc3889115d1205cb868a03e030e2fa6ae78e2a1a11cbe0a4706b0f6c381c08889698e383effd424881110c58044aaf2e9061e9ca87f626f480fec4f04ea98d3b7572092b29a0e14f1df2691be54a7273f9211fff7b90beabd3a8013d7358a73bd18bda74ee743039f8124b5d668ca5a478d0a29a2450e2b0100ed6e3254b59e5757da31c28adc5ef9c198cdf3877423bbdb967b4b99924c01640bb50b620b344843322ca129aad048a1e50c2d4f5a9ca4a032b31eb090b024f54009e98824248efa91130f335b7ca05486d38c8da718698c92a90a21a54be9b2c10f4e4e5801033ab22c608e18a45839024b185dc8a257014687279cd8418a0f186278e1c11523a878830859740984cbdb2e6aa592d690f59206447031d425891d6aa0814cca99afc272e8d6a68b710ff8f630e6178bef1809bb4bcb64845f6d6485b4f71a953d4a8ea272610c49f0d51281d58d1d605fed3e7cc8c87ca8427d88fa50f5cd51d49520093f547f23ed48da59684fd2eee4840f8c9ebdbed857f610ddb489a0571afa0dcf9d04304a31d55fe5963480117af69a58e34835f4188e1a7a9e17866ed10bbb8a6c4a91292eb122d3e67016a73947b7b997da462c35fdd07c56d6aca63ed1533d8986c54f308cf998673082f27bfdf7c98fa58f606c92a13e8122d7983dbb07238ade43b8960833ad1076f6a589c397a5249eb58ef87ed2292eb9968084868a8c8e94965c4b2ea727a6265eb261555ea286bcb4c437a17b719bf9339978357ec2fa3ddb23c854d8c55fce360e35649f9f2fe6f9a7890fdfb4af83ad3e825906f18c6761aef82c49a7c22f76ad4aed226a7c6217659f98d44e54c95ca48bd230690c3d7b903d9a2509e3a23482e9a48f805fec928bca1e6fa8ec60bc6d72e3d9a5530c83057b800dab89cab3d77839a8a1a45ec8e9f1cb57f6e770bb93804617e728996ae81bd96da4dc4117656b1332126800c9a5a10f25d003c92a9e18faf2ecbcc4e66b13caa3670fa2c518679c1f0c1c32638c73e3fafc6c9c97605711975a430924813ac651f6c54e83500e43ef43e4a19f29518fb41b74dacff3955e825fecfca5863d54c437cc00168a240cf5127c133df6874bbc6f90836ca87ce52395389b016a9f09e29bb087866658886351b65b7a30d43eecea9eee79f60e88af7cecf9b087f88a10c726161ef2893db18735f34c46c7195904248bd8159b427388e513b20805a1a267a10e127af60e6a21af6a3f1b0fba1ea2d8be2b71c2f621ecc27150493bcc0baa8bd3d2bfe8f411749951e50bdc3d9ee1861935f4677679334c460d61b0212b8fb4d4e1ed195a9e5d6a3ce79c923e81c20a6097375476ce52d967667413df84f1e9698ed16e3481994c0d865fecd38a1ac627c6526751c7ea50189bb69bc26e32ea571ff58bbd09c6e48a4ffeacf8149f96bab0d20a31ea174ed3b3e394307ff03e7a6110d6962f41cf5ea7b88c28a4f2ab5b672238c32e76b1b3f71c9d1a26840f952d66143d7bec89f18b3d3eac00f659430dbd38633d370a333c8b53aaed619cb1e26c3f865cc7b34379fa1ea2a63bec9ab102d839196af5d8b519d57086e322d650c37dea5913ab9e4ad4decdbbbbf2e322ea13283b7c135deec42fb332ae3c3b9418e7dce283a3393b8c33283bec62f049fcf2ecf14b749a55ba286322ea0cd094678f416478e966ece24e8ad14f1f325018c17c283fec9f1e8e5516952bece21f312a0ffae7abac5243a642857ff66bb9cca2931e473dfa31b8f06bb7a85d585efc8b0b0cecc770835f4b86d08774b77876a6a5e85ff8a6490cf3cbc5e8e810d4f14ce9f775acf4f0cdd26006a3df3e217e0a85f8ada30313dfb4dae337f85b80f080e843984e9dbf5b7800c3b2ed79edadd3f700a1055efab7af65ba7d3fbd5d82e0eb140ab20735965167181d47bdeb8ad6d65a6b4ddbb63935dfb44939ca1eea8a76cca890d2198f5615f5666043c018a276dcc54faa0714cac9d3b2e2943a15e5420855476ae362c62d5135260ac46a2a156d563aa934d37c50dd346167d0b047695592610e9e393e0f813d0c67a861fd85169e4771f0c303201e08d978f528a595e53c8250995121e73b10c393cf36cf5e43cf9bb94113a51da8da1713a8315c5aa83f7eb7ec208917c10e80de9bd9810fbff6b71dfcf06b3fa4286fa6e6fbddd265c9660641c66677c15851722ec1b0bbdbb5d69aaed6524ac975ccc6eea667042604932e5b4aef4f881aa5f4d8f5491416b2bb89ba5e440dbd5fa75348a5ed3b52925258cc3e1d8b43f109e53164a18fd1a79498f49b149362524ca2cc85b70039895d4d5c9e62130519dc641ceabceb2f826962576c50f66cad29189d36308269aa124abf62ab49ec9c9d5860509fc70aa028d0773a5f59e4c01d0e4ac7903c3d7d442eb4e035293d3b027e5312d09ec812e6eb4c8b7d6528efbe16834d649cb30fb573300bab5fd1b75face8483a86b39b466d5a223e9e888c79eca3c7a6e8149f66ace445e9765e610ec3090284e1c5062fddf5b25372f65043fa4b3738e0a52f69f1d23de9f586973e239da5832e5e7a8d0ecc78e936d2c56dbc741338c6b3d96c66c34f7f137976fac333e039075f3c0e184ccfac2f83ad7cf7b58655036e60c8ded2cd511c846fba2d600cf1548636cfbb4e5d351cb3e15795f95e6079f54e7ea16515e32be700ab6e3da0a807060d2a5f59b7a994f3f858de7d216c27ca643c08dff086f2986f27c67dd8a0c020ec628ffe78cc477484c6592e533ab4a5428cf3f02331cec307b8e3c3639c824778380bdca1711ede29ea9d16a36365097008bb586908bba2525492718619e9f1016ebf68c0ed57b7b2986f653cbe956df593f1d5b7437424c6659c87779bd77dbbe58b3a9e7ea9df140af15b2aacbc3a51e73cc09d1ebef20e3c525d06dc89f19ad2e9f8863bcf6b7e5043160b7476b1cb944e471b6471aa49fc13ac1076af9b849fa5f6669c3d9e608974e013eb2a0fa312938ec5226eae2d674ae3218be5a1fbf0d0f5c343d8f7e3db7df9f83a677d9dd37c3c3e99af03c39c8ee95ce5dcd319a0c80b13c73e197b6c628f60d8a31bb10ebed92d601c3d3bcff8c667aa3a261d0bc139e643d22ff61edff60bd56058db2b82b18bfd8e4a57b0faad0c15c2d743147a3df899af874886a75f68c3ea206a1895664f7d0e15191d2529fd5c7aea32496989a9c9e909e8e58ff491b327a7a6a7de4c1b5b9951d19010d0cfb7cf53ef190b6d6c6543454647494abcf4e4d4c4b4a49404f4e313674f4edb148f58d8d8ca38a147790e77b4b2dc050b71729a7e9c775d93edc3f82d2cd3a336ce6317bee194bca89ca5330cf6a855e87aef5707862857398f4fe6db8ef9ea97924e3f2d453fa6549f4a7d5956200c301991f428650a3b1c1c18cce57267b1288ad2133adfbcaf9d7ed3bb907f366edb569ec39f65d54b5409a6864b602feb0d358c4a5f00f1bb1e97d4a8e192e993677c50bf5ebb28c593e2cf8e19a7f8c304ca12e98ef948e9530af54d7323abb446d825918047d825a5c72df846ba942e619c067bf9dd07e563f2845db2939d6b9ec34c6e3a50765bd781210ae4c027ec92145c0d0adfd4972ffd09dd18bb4b77d7ca951b953431bab509e31153e55f57fd1af9cde717c276a28cbdd3901a8aa338df79299594ae9272ba8ccbda32206b5fd22595a0c4a1288ea2507484d917331447635c4aa95491b0d03176e91d448d1ee230192f3de6ebf9ed5b027bcd67c74c6f512a7c13e24c4a632228b13082241cfb64d29974017ae94f8e64d27796fe7bb9a5cbcf4b8f3ef187e93671582f6378096a9ae673e2e494304745d2dbbd64997e4ac7e9584db554606f27d27ca79dc1d6f4951d59790577d857606bca68bfa474b9496204714312685c59030b994cc18b0d4932b011870e3ac8a4cf125266d8610839e80843269dc9c6645bf824095db1f286914cfada48c9d371375b704aa94c3a0663c5278a6ad4128dd815a3cb396a187f62f46864957e76957e7695a69cf28b3fec42c94e7a528221074620305ea9d1eb7418a87ee494ab54be728e8221abfa06865593015df105eebe24b8faeabed655a9665f90e391c7930e4dd3341484215a63c80a258270854c9b0118375011638a1c51649a94d7b680e3b5edb52dec78ad874cd3342d46796d0b315ed3b620e3b510649ac6d3c41569f8f0823482aa38e2c1610727a87823e98b2129bc70c288057831e48590fcdd72c5173cbc55d43829dba68ba467aff125d2a485531747a1cd1c918b375e4a3248ea68975faf72a55796f36595976e0292152418c7602ebc7c57cc6ca004d734d3810cca48420d2678d0618618e669037fcce2db5bab810e38500185174818a1e00754aae822480d249ad80108556adc162825cbb7d76c3a6801e41c7d2049d9e165a250f1ed3f6a103b441a2fd6f876164f12453c3147962b6198a1034f146fc0b1c4831733e8f872c50a1656a22c9110e21748331ebebdc7d450a86fdf7656bbbb3af601293f3ff0c086208062206b87754cce30458e3994a0a38a1cb2d6e1bbe7d4664be93c523a3292f1ec92eb48e2d90819731d3878b6a388e7c20e379e3d481432f6213877103d7b09ec396162787613380c183fa364e54a0d3df1a64a3a60f0ed31ab74b3f5f6ddcd822eee48c2624418a72bb24622c20c2c946270c1153090b5956f187c77912ed2311637b102dfc49643b2958389672c9e9d25c71bcf5ef30412cf5a3ccfe1c63fe0598e29cfbe4ae930ceaa51451337f8820839a2680303327822630b9529ea4022071f8dd53555d6530d4af8f6544aa763194f4cd43cbb97d2611d8c98d2136f5012290e30dfdee1100710cf2ed9b9940ef300471c0278769ad2611d66f0a3ad121c59be7d83801356b4c8524515550821631b10e520280636ccc0051719ff58d172d37d917e4597520651599105870c0f7efbdc7450e227f6cbcbb74bbef2f3ec26b04f00c90a2ccc31a69406f87e8389a737d4d063d5b8e1c6b3c71913006e68a325329ddcf8f2bbc58da10fa9101f3dc8ef16378c9e0a7deb257802aba96e033d9de1a68d4636d6401bc4db18e31cc1763ab12bc6e858d9e2c3d9349bd8359d2618769940886f0f67934c19dffe256c2a328ddada7c3beaf940fba8b5b4110fd55bedfb9cc6bea203c169caf1314699719353cf5608ffd41976c506c337e16c8a4c3f47cd257a2771ccebc2b13d8ade5ea23713dfac3e7a7fe19bddd2c6d147efa676e298278b2e9b67535ce263f4e954349fe21351f848f1d1b99f190069118a6d0cb9f1512b52e2a3731e5d335ae2a3531c3eba7684c647e7c02a1f9dd160b958aead68336aed878b8cb8888b8ab8888b8c8e929658099ce84570929698589bb9c13721fb3cf1ece9e9e9e9a9632b9bf9c427166a63bf7c5938df14077826e34024fd8a1e515d19e74daffd225b19f57033c0f91ae1c0963693693e7136c92159248de451c7e2648ada4cf3d17e36a7e8db53a43f1fa9cf473afb389ba6d377d4b1e93459d559b3c935a024a525a6d8349bd815dba88f3aa995660065478a25c2de021ff5d16c36eb9945b144da5b706a451f2adf7e799f277eaefcf8c812c694246cb0831843cb0697227c90411d54dad841165b112808287a5c220ab10bcb6cf239a736a34202b801d36e4c03f9099ee0f22b642dcf2017f6ca5a22512af52b898b8a8c8eda3b72d10a6132aa04a9e45d77b5d877a324d18319cc90218e2853c80c85a181c9075594a490ad771136874d388b9ea373cff826822dca2eeed912e9d1346cccc8bed889c44e621dc551e4483c370d517dc8e15903683c976063beaff5389ecd78ee563fc137214e77b483a9a954f1ecfdc3312fb64aec8da5e879f5bb25084653f604f88671b6b8e1d973865079f69e35d00fe19b5532b244da87592b64637c854c2f6689c48f9aaf9008ee14e957470edc31d2afe85d7f21cf9ec33814aed2af52cf8e929496989a9e78957ad6402d34347de60ff7149a403d5bdaaf489d0329b87dad2320056bbfda7bbb487297838db1f28415b6aff58555a4a0177c04c3aa7d08fbf5220b5bc759cf592fd2566ce4286a8ba313cd51d8ef3eb213e99bb7f7d5786b5f0afc382774cf72f2847e084b42fb13d64fb09e5137a9c804f8c6eb3a640eed45da8bf4c228ad95d9877c38ecea2e6a749ea2b61709f2ed0b2e0c27a7088fdf2d40707ad6e20161e9a3c3be617c0383e1e414398a59398b5d7485b00f16e6ac36a774a2c7a50a65876f62bc63fbe2e1ed5f7b10be5179cadb87f08de7ed387cd3396ae57d42ff37f5f675ea06613cc002f4cb442a2c0dfcdea532e4f0428b38e028aa41d64a414a3cc111c30b988042d61ef37943685167d66f5708fbea53a5befacdb0b8ed01ec82d80e6b16cb5dedb01f7aa55b1b56f9de8038fd6a5f810d762d6e31c82c82ab4095ef8ce02b70e53c32d5573f1ac6683ed5301a292df908467a001121c94753c75a4f1d0be1f356293175ec87b747a7687414b544232a4d49d848c7803880c69dc81d887fabcf5df501f12fa502cb69be9575dd9444a6b71389b145faae35f8de5a9bd6db766374c42549a98b17a62f4e9bc6434d3a3a7c8957aec42b57b6f839e10fe34c827136a3a1dee3239abea926146c519c25c279fb023616a3582e1a711e46a368148d8eb8242975f1c2f4a5c9c9434523ee41718ed3b168148db868b4711e53db46b3f9062e7b387cb3a4048e31e957b31455be13f9e136e9386951d0880371e9345f4f4fcb7d78cf0f677907e4fb7c84af878bf0d1b87f3b444768fc73d03b8d9ad4b11e47db12d9b65e3900fcc8ca0100ee106d0e823b3d7ce51b7844e51fb843e3dfca817c2a1fe15b9908dfca6abe95b562aa65a407cd7eca6bbe196f7d3cfcc727e33ebe986d03650f0f8ff11e19af2e3735651276753cea577bfda252bf9aa95fedd4af8e49df4fdfd1885dcd6409bb9a988607c53cb0746bc3a954295a5d3011e88f1f1e55de3ebc3d0e758ce53d53e39df25e79d37887e05076d8d532895ded5064c75cd4d5a9be96a7be1f5ef345daf21fbe291fbeea44f8883a9b1ee0f708df5a0ff10dc76c988ab0a3b5864c7b2c6a99a49d49c77a88da48c74ae812bebb8415d29bd2096185ccac90de5c848fa8f34d35d9c0d6f6c3a51be9b18215d2aa8f05f1d59e8a529e03515038b67714cdee88d1c8da6350c7f41ec52246711d14767161573b8a66f9e1fcfdf0d4e7536529577d3d5360329f2afb0142e957435921edb28e4ac16d8d7a4c694ee7055656a3f9061a69b57e380a786abe9e1e96fbf01e1a5f79e75a22aad6a7f21f5fca598ada22788d231101dc7ed5802dcd6b7c65485a1ad85241e5359e0195d7802f101df9e1297fa1056620e52d6f812ffc00b75f9a8f6f6553c6637ce5e3abcefa649ce68bf199cff3103e1e5e7d155f7e2d232a55ca470fb0a74706cf79784f4f8cf7ac3c15c2b7b2994fb6af84fa55d4af76557b6d97f18ca02039c2ae6e2525191e8e2b12281a9a558f1eab964743a3e2115323c3aa3e787c3d5ce6a3f19a2fca562ef3a9dcebe1345ef3ad6c5b34ab896a71342b54e7751daa43799ea33a94d7a1be2c3d7af86ae534ab1eed9b6a71a0911e349ec35d4cfceadd8c1fc297721fdf8cfff87878fd649cf5d5f9c3c77f2bab3c1cab8ce8d6a6a7f39e2954167d4a95c5f4f8c064313ea5caaaafb346f0114470208ef2adf15d4ad2b1954b87d2b11e3f5c86e0d29174cc5534eec325cba564015c815d7269894e71731c6297f4ee56df08aefa6adcbf28eba2d7e38b719aaf3a6a04af719fde01f988f863a17ac8bcf46e4ea9ec71cc86a9083b5a45ccd628620d19229c58267d361721e2cb1b81d9f0d26b68cdcb1a6e85d015624765e714e7403e220e6c7135fe42cbabf7f85e203ad2f2ea2f88e0311b582132e6e340ec99e2b28d0908555101e196f11dd879280f3c81dfeb1c09c7f60e1beea0b1c1e70c1b7c1063c3eca5479f9549f73a541314d6fbbe88855dd23dceb272fe92b04b7afcb2f850d9caebe75365d563be9e293059f5a9b21588a45f12491d95a754f698a29f13ead3bb1f5386cace2d0e3432c208358e029effe13e7c7eade919e8e1319e011aafded57c2dea409c4736c2d7a2604b8595d7f89195d738f51a7087888247540e04dc69b9ca47701e19ac5594450b49688b35a090411ae20b47618b2454f4237c3b4423f8a64e00e247560e04dc21e27c04704704b045bd87c7f8ca39f088ca6bc09d16d8a2606b822af4f0183f42e3d569bcfbf1f1701f9f8c87f0a59cf5cd5c60653d3c5cc63fc6abb78cacfc022b5b7906ba0d99eac7b7321ffbac6f65467a7a6448f98cf7f4f0f09e185f798f8cf75457d17cdbaf0e614a67a15fd2ebc7a45fd265be38eb97f4992ffef44bfaea93cee3939e8a403f23f3ad6cf5ad4cf5ad2cf5f1cc90e08043d8255d8e191e8e635c307b6a4719d75e613641611f09bbda67075497246157cb51b7746b732493ba7463fc98c9c1924a51e46000464c5c9143ecf173146c61bf78dcddd9f36c4670eeeea66973c266154260f9450202ccc77e9180a8e3e57a32a55d511709882e1fca8e44701983ca4b9f913d9b67b41261a6c720d44dc9fa00b6ed93a01cf6e1a6e13c52caae8361275214c8813d5384c838f7e121a33fbc242aecc399df6fe679fc74cee93c82229ad32798257ab860080392354316c3a5e3fb8a74ce000939d81226dcf96dd261625efa5a01f34fe26b4d3a4c989f60961760d81bfcc82238730986db33cfbe290eec0d7e640cee4467f5ab67fe8d20288480a288c96752892a454821620a59cf142132761f1eb2e83d535846ddc7013276eef371808cee101d61df41d23b444738a7e04e117087e808756ea7bd70cc9045161f849104195dc856d63c3b6c314510416c5972a20dd9caa28738473c87b539a59ccca5f1a5dbc1ceb973fa82ad699ae7b0a6edcaedeeee10b664075eaa49fc056996ea5403598c3a3dce5ebaa7c3627cd85a774f4dd3346d6e2ed9a5699ba6cd293730eca07474524aaec14423727185cb4a33e3056afce043b92b89eceecedd1fb2a7b27b0cf2a136a7e6da0a59dea1bbf204dfa03ad4e7a0c4f4441ee59decba0e0cc39862183e15a20086ef3c3abb949e7b6098f2762d7aeae34f49045d88d343a1109fa25c5a80c8a7ba30ce7eba06475da41c9c5e7ae75dc75e4c31f5a6de9d40b7a553e790accc5e3ae5542af5a54004e5213d81ba4ee7390a0c39e529308c8feabc03c3ce9fd05f21284751efcd7789285942e43bb00522ef04e54fe8ef0a694779b777285f219c6f4a02d2a987d1699c7774d1d746bba2b2c7219ed90a242dcf1b90d24a0de5d3962d93f03ccfb75270d574cb31f24eada7f61b35acdcd3e34f300c9fae90e85bbe3fa41d84d8c55dd6e5cccc5186d3c3deb6cd45299d73b2344dabfdea6eda2fc9391739dfd491389bc98eec918c8b577c7a3e53f966c150bad7ed576bd855594c33dfdc449d99deb395829c2f2520651ddde6beb974f9e9483a42a9f52e91dd5669ee393c37b64a9bcf2e888d722a5140390a44a150a8e5627c448fb27bc694148bd6d96948e8d71986a5465f66a35b1b19ac632da96f655775b9e3c0d855244b0e5145d246a28c896aec8147e1e20f3574d1013ecca1fd308bd48409b00368c4f8aa45109d87326685ac2ce2222a836e6da203ba06783101ede147590132df1eaa5e155d94c5375fec0e05b68aeca39ce7b94ffb42efb5f885503c23aa6f1ccfc7ef496b5fb85d5e7a4e4fd6010f6fa579aceacd44f666b6283154796666e68ddacc546fbd991ad62fd20f0c3e647de8cd1f9cd083131453700102194ef01c2187156d5861031454542124e50d3a983749ca19538a1a7187359c9c60af494b2e58964d0c2a8414247945174647b9a0ac9c4d4a164e4a11ad319a52a69894f5d3c373d720fd04750700931403212464e0c2081e29416022061b8cc088e306190bc172d78f0d3f33302cbcb61bddd07fd2a97d4fe66f921104559e2e670ce54fe9da94524aa56e6bca2f8eaccd295beb3967c775cdd85a6bdaacfc65ab8dcdd79aeda853df9b90e7a3731cc7291dce867af23985e89c8dcd6fee7b139d52ea9dd2a1361b5b80f3185dfb8cb04b73fae93859815d9a236197a6790ec767a0ba044cc22e6dbde89cc49604d637e7efe79f516a8803856f42261df37cfa0edf509f9c4f944f4a6b65b11c85f5cd09ca398faf4d8f3376f5449f48e563d2afe9def4e973ce39e79c737aa86fffbd8f67367d7bce3971d835635004c2f9397d6d26a8240ae1a09bf14d07396a3acae917bbd723af591b2fc908cca8d1738ec21cf776eabf79077e2b04fcc0c635e74f3400bce6df9260b38294dfba340f40075ea6302d209ff3ae37ca96aa26fd1ce85fd5a4f73a6d372cf9662a96ccfc9c93a9c908775a65d78c8c1b6c5fd1bdd0bf73ee5c7ad77d33deb386e26cbc96b27dc9cc4f90763718845d51898328120e3078d42f52953bb098e1f53ef49606f08b84830edefb45aa12042f66bc6e9c512bb0a77459beb4bab809c847d6be622c875fd197db2300b06c6a587e64c362d5e4408726258a465939ccb462c3cb61499901c586579305e5d451608e0a82a630e38d1bd690e20d197f3182888a163498e1c419b220ba6861684b709eba0928e4c4589a941ca2b0e15170d92983351b3804e6352c1b160e4868dd4838342159ee6afd22e150e5b55f241c7c3e842969d1c929757db7a0750ea94a9777fd225559e2bbd59eea2255a1f2ed5c975aa9a429d47e46d9a286975f97738974bea0f673bee7827ccfbb2fc98d6fb27acf3b76faae7ff09d04f3dd54e33bcdfbf2a8140ae077deed945d775dc75d17bbaed3b61faa9007cb778e54a5078ffa785ee521e7ac52323ddc3cb51e72aea95228701e5311a080055460b8ce39a7d3815cb2e8790fdc15d2cd39a7ef8fcec3e972fa824a386f57327d3b999a004f15bf1b44a628c0797360b8ecdcf6a180e7c10f77ee6e17b537baed98f49531166cf810c5102c343cc9fa5b59c7414eb0218301c5c64ebc830b26190c25001dc1854c7a7f3b718a0c86bd422641a256749e8f31980124838184398850da89329c3193c100eb59a30b7ec11c72547992f5276d1043d66d9efc769606464a32183a0699c70a88deeda6452efafa4088307c64fd753690f1179f8790a8ec5091a828215161e299b6435d731e19ca7954ce23abce2393711ed98cf3c842709e09b68ecccda7f3c838e791752b1e190f0fe1dba31a7de6ab3c3e960caacce7319f2b7afd60ab0fa78c1ac2728a7ca9e11e19f990f480fa5848e254c36562024506355ca78f31882906ed2b7a0c8a4131067db41249881e83a252943288637e842536138dca8849471f3d6e11bb708c23d39568c5051f3d5ed1a2952825626197500c9212f4d1a3941a66bf008021864732ada2934a5d971f63ec96991c3439accc50a452f872bf5ba220f986be2104b9532bddd2baceca29d2316d739c292573cfa08bbc52caededed955c6f37ebb2676597a7ed4a29a5dcf59cdeb9799b5a304cf2ebfd8538a871bf25299df56db9350dbbbdbb51deee6ecbeeee0e79bebeb12e512f3d968f9890e33e6a1f085e7a7f51a39ad7ce37bbae9e5be99472a31a1a949b63702889ea3c8f5381f4bcdd9cb7eece43711dddbc0ecbb6ddf9b20bd55e83924a7f916ad8e2819afa45aae1899a8afa45aa21091458630d42743d0cd113d4d52f520d3f23a8f517a986190d4ffddaeeab6151b94b2b0d5d3e64a1589bc2aa95c6c8a3a3491e651e8ca088e1c127a8b543d38f6745088986282b2b4f3fe09103130cd65c88c01ea57576c5e7ca4f6539143faed4b1434f65249fa195f7d0437ba0c2affd9056998f10eab3f1ea51249f590572001b5900a0faef163690c8014aa1c6fc6e61630609d4c181ea3951a076679c50696e50811f5c11a4aa7eb7fcc0e889fcc009d90c4f333431f3bbc58907a41994982fe8c2e8b55f1bc3f53072cfbfdeaf6defb4df1407bcf8f977edf1279f59fd72fa27fc7285acfcd95fffa638b047b266661a77eb1cfc5b21eb524ab969130ca3b6bb0bb2fbb7ecb0b9bb25333333c75f8622f217b2640fdadc505474fccb11e57108d4eb4cb1c2c59253971d8e90b1f350c71608b3a4198060c71251e440167f7876e6c2497ce3f97cc621e3d94b0043e90ba61adc60c411b211a4401ac28e3053b29821faf0ec485660a1d9932c61b2e5d99f40e938eb9a971168c33294c3337d5e9ad2c3143278ba32832519f36411e2810c3070d0069734641c812a752e4ac11e9d60bf4a3cbbb483861a30352185b14610647c864f105454a186147364e981bf73763edaf29e2721f33caf6bbce7214d89e2bd2dde439a4186f7dc866f6adea31f69951e2b3d7d978b0104f8ceb9944ec77cd4b12a830f8ffa1ee5a8940e2a8e7ae4c4affd9c304f4ab3251fa61f7eed8c9d7a70f09c734e533a5c8c841c20cd64dd1b4e4732ea5a4a87d60dcc6fbea574367a23c3f49acb948e26670c50fcf499d2997bc347b3996b369b45719acd64527a7c7414c7906fda458d7ca4f4d28baa1fea2abd7c9c4899a268a17992835451944595ac8e19e957f4caaace72d73e17238d45da1b1e0afb9aefae03368f0ef071d936e93697fbbc24158c2c917ef64d838cd7c1b393f5197f2210bb8238badfb2987993ab3171b4524b347a62f3a845283e118d6271280a4519555a39779aaf40242a90a626bbdc51a1ebe31c4964625567b91c49c756c6d4f484ea58ec43ac5ed4b7ac80e8dc87f36d531e8db529ed3b29ceeeeeeeaedcdd5db93335eceeaedc6eed8b13721764cd14e919967be17d437f3afdf8392be5ae942be5cacac58564dddd85f16ccae508428e93ced9d4894947aba435c992b04aba4e4277b010d5ab880c50fd6e71dbf8a828e8cac6517a5b967b2c11a983efc8c5b7c72c7ec1a8c5888a5abfdddb1b8d0576b5ef4df4dd9bcd4790846709df68ce557e9dbf709d581d8b4ea26b9b11c7e2143f2ec8c1fb41a80c29dfab2f2ee8d2e4c46d1b97a760e82348b2845d1d3f16509d72c3414e39379f9a4fea734a9f934e4e931bad95c5f2a7200dc55160f5139495559d156f50524576151887d8c5fe452c54d4306249aa3731372b8f72876e544a37139459f18dca39c6d98dc459c77878aae3b6982fd66ff5451eeca3028df48b3d058695e52e663dafeb43d6fc4d1591528eca728c98a3965ff4ee8e31ce6ec386b96839fce270cee99e53846f66d75967ad6058c4843d929e6342911967e714993e25e85d5143184ccb9194db2f529096af69706a4c1c29715c89749c1b09525aebcc8ce63925b08b719e59f8d8b587c3b13a9b49ef8a1ae2e08443c219a3a3c39021a95fa4a0a40f6d9e679464381ab22be7908f328b1a5656957383fc22f9f0840f583e74f9a0dbb93bf3eb51c9ca9db1e79c73e39cbbbb524607c2da0b5a33fb8a31139ac6098459739c124ae0823f40e5ac18a98d4308eafc45f2a18637800f3e1fce7cf4992054ca99369e5d3203d5f1ec362e76183b0edfb4dc052406100e66c85001081047d8ab073100f68218c034408080bd0620a87310040dc224fd9c91451d234c21c4121858c4e1344313258c20ced6c7983a414a290550e52f129016432abf2a0b8b97d27dc1904a70a6ce9794605873057735340aa0a30f5d4a41ac840d69a7950b2e8c08864b707ecec99ae2db6bb6c0e21b25eb8ef2b3e90622e2faac07f28b0454e57bfc22f5f024c508a377dc4725c7b9d69582db06d2c9929b95e8561b62d5791e874fbec39d5f7983e1e6332f650f112ae5dd397514b8deb18742a13a507e777777777777b7bff72991de9b3f73ceb11ea24ee59c0eb72857129df30e0576945316f092a8d1f73bf7cebbae03bb18398ea334721c9d1690dfc515c27114e5027fd7711c8feadb94ce72509e7a7cba791490ed318aca72cedf465b9b6fddd7dac01a7609d52ae3a8e1f47d56c2916eee65f140183c199106b624c04ade798b6ef9466a1dcf39e793f99bc7a39fb3a1272127a0a6f8c3c474d49414ee12cc715c216c32f3d47c7bc0f74fe90cb6007b2591478bd2080528404193264d54a00215b8e0820b50a04071e2c40993c92b26d3234b6632a7641e24c77a3666269a3d651c9b8c274f9e84304866e6c8ccab14541758324b3a7b7a136efc6a32186acea02787a8d2956852e3be70032947374ddbbe0d0cd935d7a22c1250d1722441d7ed26e9967be537674a0b4bf8f56dd3529493359dd2f196736a1bd5a8b4f1d6d9b536ec5a4ddb369ad2d98f4be9a050a830c84ffe09eeee76e4a171bb32bf507e0c2b03643c8c2ed497f1b03f469007959e2fdfa4bce3bc53c555ea4b51c9397b7599cfc993ea0d51a3f785ded398989818afd65a57abd54ae59d77aa2f6a2995a78aa98f73efdb25e1e36a4739f084e9d2739a7eedf39372c67338cb4c8332bc75d1c929d967fbfe9c735345e4c6bc499e3fb97b7a0e73d7a0932ea5d87c1794a92651f60f0d4c5d242b5e3ef4e2e6ecdc12d995311a18c54f70865dd16d38e2a05b6da92e120f3e4f977299c50086ed14f4d83551a05a962a5d493b75cdb7467987fa42ea5c0a8cfa42ea1c4863a460c854fba4cde4243a1492bcdc4dd334ba94eefafa6c8eb23f54f62804f4235b4a70f352a3a321638c31c6cee1e698d25aa5ecb8cfa392f31b4e7293d3386e636dca8ed56983382358bbe9f7cc9ced73f6f67cf29af7945dd2c55897ebd34329d7e5ba123933c339e7da7064ea64b7a25b7f2c594a2addaa72ac8793bebeb954e23f88a694beaf494d6ab24d49a94949a5d424953ea5dc99a914e46bd2670b344df3648cde4ad8c1d6e20ae1d8ac31735c5733cbcd88ce3725ecda0f2f89cabe2f993d85b94234163c7b5cf1ec1974f2a4bef4b92450289db2809ceb92cea9e0a55ca96ad24f41b942640b5eee3c7bec303a273cfbce51de754e9da7038f48e7bcebc017a8a79c035bddce0b4447a8a7fc05ce555dc783d37d4690f40be5f2535aa52e7afead4551d73c0352bac6b9d4fc05e95446f77376a15cfb5cfd4239fd50f3a9c694911e09b6d6a5f3c838b0b54738b0b5d28fb4166ca9a0b9f423d337dfbe17524e5d7ed45f50b554e05ce51ca09e92817395cb403d25eb525f6b7d65aaafb5601276a17c65338e1abfe5b9f495b53ce919e05c527f413ae719a09ef214f8421be9e9a931ae521951c52376a192f40be5321f937ea13ce68bb37ea1bca27c857206aa31895d28f7c0c8855d28944b39ea7a188f1e259d73249d65e35f1b9496d4714bc68ea463ad8ebee00beb713d82ad7e21ba045bdd2b7baad1d7e3520b384b70705ac071613e99fe94c6990b93fef08e902dd8bc1229c3cd29e71dfdbc20549b97bff9729f7c492de042fc08d2166c5e0395cc13262875e8e6d2b96dee04f61bd864f5d4672b85289483a7ee04f6147441f6f46e869a5376cdcde536956c9376ae7d28d738d7a84fea93faa47452d9649d66b37f22f39e773ff1de63a27d4e9a3ce07985447fa2bde64e347fa24900044fbd0990a7ae7d4d1ef07185449f00cf537037c8e624a23e27cc49aa6daac83e892fdd666a3178c10920900002b262063db002873776d022ca8c8920263cb05e778538a288819213445504d9e0073d60e1c40c9cf8a28322a20facd7f5404c8c1cbeb0030b2f8ed8010438183dd1c41636ec884b7440003fa8e1a78a272a4360c1b3c40f10501871a921065e94f000cf0c49e0b004972dd020238d2368186369498629d0a84112bb2ba43046186100614b1419e4b0714c010717622069616207487860bd6e07e078238b1a3788b27485cf3e91010510aa1035cde06768c3e0810e292ec8420648080131061153eae8a2258b24b27c80053a9c90bcc85145195dc87878e8c1082c37e8808a133738c203351dd8b8bb82d350d1ed12e1f5402cd2319a03031ea4b1850d68c8604516bb20438d296cc4008c1c46b218a3111f61b18aeea1d65a5dee364f3db6b187eadffa12e96ff72203781681a86190d095d848e3c8dec284180c494a87599800dff4f450fb26c05f118f9d640477d4d62fd20e3d3f01138ab4c3d82995bdd18d7a4ecb140577c768c28b0de0b0820b35c8e8d409311b0c3f4e7589ecb7c76cd7c58f4be96cde812d14f6a3f3bcf685f435289a7f9cc6f3ccb1d0319811101358b820065db8216b67336a18e4db839cc04fc12631cccf2c357ad7f86de093e94506ac93d7048bf58a44623e7acc4c0d810059214fd4e81d8b6f2291d5b36c7a83518d1ad6ef1767d97e0dd523ba6d1b6a28bdf376f6acd832f49af3b78ff27073cdb7f84e9ec84779cabbeda417414f6bf9759cc7290a44ce233318aae051cf838410610ace5fe4a9e21bdc0e157693f81bd215375e024f242025bb5078125f9be017c2f0cdcf370cfda652e06f7f1e549c34ec40d334d7babb5b6bb91a18d6d7b8b99bbba7a6b1767777bb43d8f7b6d2253734ea3ed222fd7e38c24b0f41464dd3b4a8691bc20af9581cd69915f2922c31588b31f627e532a1a6f4604b0a05ffa97d4ee4ec9bec4409b3200afaa32b991fc72e3d7ef1bb04a64ba8740c17c5418d2a54340200000002c314000028100c088422d16834d4d5ed0314000d88a8426e4c994ac42889510619638c218410000000400066666834200b7712133ca5ea8871b595c2851c1ab70474c9f8c8ed29a59f10971cb6a1a30690d6dc14f3de4658c446feed902f5d33884445b4ae86a5f936d5d07d5f42651f15e2ab360b385a7c9c213077ffaa5b86e9a5516ddd56fe2e1f131aedfa323c0764adc2e2366f41500ba33fa34e6b4cd94d3d9173b782280358f3a0ed6f2c2081e3a6c168016dbec404e74017b9c74b40cf2e1b80648716273e20b6a53763f10cd3d2cd475f635a68d42923028bacdbe73cc4333e135d0bea44485a67ad865eb6aed838c823e30be7343eb297d830658ebd0637218f0bfc1ffc78c723a142bcb41bb42a2f3e5835cf116436f14bbc8a80e033f5b2af3a326b1fb52808bf886d97b9ac41e5831586326b1c4356a9f2f99573bad8835edb06cb478685b9ea29f32712610a590a60f6a7367712abf2d3cfa56ea7f54355be73858e73397e967396ce15d6e0e2975aeb998e1cc99e04fcb17ac827c636ac4c774f527fe8571886f3a754e65638e024cf4a4572b7be9cf10fe6f3d92716f306f903042d9009a39b2a5143752a96aca414750a458a1306c495c302fd02c9ba5f65216511c89d5bb46c523cda6cde7b5de32ed002ab8be15de25b587f9ac78506bd1405bceca70003d499874c9fac9144bd2c0c06fb5cd27a7019efbbe5b04c5a99b8182e97cf13148a1865321397d7d7ceb6d572adb1a48f293731c6974ec7c9a49d26a3fe8a5888f7f81a9b8b81516076dff7d92fb2f55cfc2f65e567f5ba65151efc7d35c4efe55ca8827f77e49777609858b46c90520b881a0e8b08d88859544d64c6432f5e36646400b0a978d8c63c8829b8c983c17b381cf42b182d0370218ce52dc5d0eda317dfa0cba4d7bc96bf0c66f36f6af9b231df49b73edc1b267eb90265d2dce58ba48a3702141f3fde113d2995de1a331e39701ed8a595a499dfbfd207fcee048222903c162b13c350ce8c6bab8bf84a0ec15fdf20c3af7f8d187d2936fcfbab8d9f1ac2d53e2fee9ea0188117c16dba93db7956030c8adf36d8ad75739ad09fda1a232c49c9d8318a6967078ce12bd6289e3ce126e3a70fe97190c59cec69fa82c82abb6991f09a22dcd90b2094f98ad43761840183d4b663d66a11108ad2532c8f8fe2fa2a99cfd7d64394de54336ee322f5e539db6dcadb1d17c1e0f9f722ae84a4529b3d8ff2128763e9195bed02356d809adf08e2b581cb4efe32caab832d6de1d07235e062a2dcf3d6ec131abb291d02a2e803e3f5c4312031f455cfd0b677d63a83e7b3b8e6761485b22394e79c60e9d5818649919af855a4451e291a3adfa01dda2969f6cf679eddb8cc5784df65c48617e28cd0391405ebd4260d8f1f295820807f0dff6cb21c354857acbc3976e5adfd5e4d84c2584e14caa3ec11f51c00c22033f52bf86b7685d655c9ad97f31d2f4c56e088e3b40f7fef5f918f6b834558f5000d28fd20fb0b2b0fde7323a37636871174ee774c8c3dff384fc254cde301eb89c1eddeaa0d8a505fb3ca02125567b21cea706b5ed5320c124aa9d7c484f3286636f9e30da6d6772e094d6f6c6746e91fd256c9e24e3c675609b1a626049bfec83ce91703de99125642a6a5363915a1962492fe6e522a3fac914f25cca49cfd96733c619244bf6c68827ecc92a545af67ba2719fda2f365e2d0925c91f4019d1296e9caba15f75f61ed0960377ae8793963905e8e477b74e00d57a9edad19f3b5bafd861eb7226b6f8e3e4d969e2b82fdc08da56b88bdd48339b41ea7281953f5bf9298b8b9414676248cce3f0ea9497b541ab3ce37a56bb66524f2db3152183d3bf6debdcec0620d5ac0cb63ffe28c8e956d25bf4901f42e7d22a055836ec79e9eb026fb3108e4c751bf2dfb20ef8b9e619866bb2a51a276bfaf753c804a3c010a9970f7bd04c370eba47c6b642bbb923612f84e3803a792b551178fad0e8190d621c9c5e3058a5a807f5013f3357863ea8c8b6a9880ab80732e16256c9b2e3c416dc5fd1f558881cde19c73e16f3109a54e50a43890eac2e8ada0fd6a035ea08b3ab9cbdc5755c0d2e58b803d0bbdb5523b5cf03e9611c45282fcabfb532a3d3050f48371b5375c09182c6709791a2c97e0830b43a22aac71f8a14c4759c239e98a07f6d22f4be80dc8ed7ccf6202e73c01cab8cdc285231c13f6151c98e7ed6c0621e666e0b63f7c96f5186c6c236161fbf9704aa484130282783e23a6d4aa190c8e7e5ab9bcda19b8d5b0bfcce5a957246d654098c98d729c7ea83e5c300699df0b19d2484b9d411ee35c0ff1e2ceed342c6c348ce65fc8586a8cb50472a184a2615951269b5e021adc9bbc7a2062f1c6e1ef2f920250ba749b1f784fde8416985dbfc02a8fe4c3e4789d42f60cf36e0644a42edd985fb6af40dc4d82528db4d4cd5c148418282fd9b8e37ec60a7fa2e0555299d1a1f03fe41ab5bff920d7597a0c3e098e184385daf5cf2915ffb9d898e6800a41911890b0b0e86085d6d91fd2548e8ea1e42b2e5d901236cd1b0fa0a08d346ef7c0609c1081713203a080b560a0ecf24e0a05ea84d1c3f9332be07b9e557d8a49f19f5a0cc0529008f5df574eaea3ed23046c2bb1b8800dce60ddddc8e42a72e78ee2f6c594292710e8e62a02e96c506ed9ccf274c902240e04c73f5cc3973f0f9e7d6ca668b60224b7c8208f92646cb0a759bb22373e2fe21c167b7be467c7cd1b288e8000113499ddcfee8426123c6cab47f1a322d873df5258b61ed78d024b15f8c9cb257af7d4189c8cd87b00aeebc1870af30630f27534a5541eed4e32df4ae7d19f6f0c555c2ee7e6b1847cb34ff2ace1d98c94fff3d6d71e758c4d33b2e94478732c79937e62dd10d1c14ddbec6d789b41386094196f4bc63e51feb1f7737ca322888fb6bf5a9f8184e8a064837f2344a3dd2c300f225ded8498fdf92bb5f2effcb2214a8995705711a3e12f21bb122385f870266f612d4485db6a2813cf4d106356033d5198b3c0f59bf999d0d6b53021b57a8f86b532474fb52ea8bacf7f368a98aa55f1b4c2565b2c24966e7338da645acaf5ebc0058d7497824763c88172e0c2e171486efdbb4bc1737b2862eb10cb624c4b50592510be1230ea391c215fe4ec8a46a8ba7a83ff4a82939e6008ae61b8598fa98cbf041c7a06457cf36ab480dc820f616930d13334e4dfa731eaaeee45d9b6c6235a08e8c886588c2aec5836f4c3208998aa466c3cc2ac9af60ec596ed800525f23f7f3e25f5cce6b3420de296122c2080fd922fe239982f9af7e96de789ce534c118403ce65cdce6fd5dc1cc01cc6f4bf3c5a1f132fb9f0bc5ec5e829998411e5cab88fc6d077d121e7bdbf958fb2e90d1aa457bc0a76e1cf25f2f73a00bc233320c15b7c2ab143f36a773fae28ff6fae9833da3804333c1124c0d9aee801198ce251d942b5a4591601e5efb6ccfbd4bbb3aacde567298a00def2b4c591e9f29162e2daa0f425dd1ab782e99a204772aabd4e54761b744016ba377ef66f32ae8c3b1e913ed208da78f20540fc1dffd7e6cbf21c520d1846e49798668033c2878ad539fc014bdf47db8c31a899e91b4f062f8e9d1349879d7f65f150ecbe63666c09e5a5185dd71d18a42cc71154cc713171b9235edcc82144ebd06d5c74c2940c9c1868263af923328bdb15f231774046f5a601eed293c00ad5c3a939b235a5c1f03892c5aa7922e8b538ba1c32228bead6fb22455725d86982f95a28b2c620409529245653171fdbb122b31c27d00e2e2d999d9b8f4bafed0abf50a0d9f7f1bd2ed9e3126ae8262844b2fd9b0861a169883a5424719796520b866104832917a4a2358e082d4ba922c6de7ebe8a69efebaa8a6a730c7dc303bbbc891d6f830d4a790687683c35f6ebe645d119e8689e3a0ed31a03c46d778724f7e89ba0fcc5e1330a392c69b17289e2e61143a85b70f37973ac5474260df640a099e9bc4b42f30a1bd09c1eb4f0f6a44751dc7d787b012d5cf896f46c7bd73717209fa1735fc6110577689eb08cdcbaa3ce684cf874705b0578f2bd9a5c6a4ba512a01181bb3628403a4a2cbe41d5a7b2bcd62d86aa2367e7f9a7d3a4756be5ff1b265b703c623a403802b567a3432727da22a13fba098ec21a2b1eff9f876908800c05b7a5127fedde9720928b23d4b4a0afb36154572504e235b4323db4c7c069d96ff59619413298827f02755f0eb4ba64c99855dd2401d0ff79f7379944a3582d722184be84c5a6a3885b721d221fdc4a8b4db6b2d3c3d22d6bde4e21229e5e22505975529c601d9a00d7b095721f79a49017c2302a5be9ad0b51844e86238a3efdb4d991449968042197a6a7d0894272a4b5aa8152e9aa84caa6ab3fb51827867d1059a2e52aa2d09d5e218dee8b8e5d3aa91b3c13951fda3cb9a7632690e83dbd50430271673b744316337b4663701e186621ae99ec9b70520b74c60e5411692017e9126a8459d840f5447dba25395a974e59f7e808b1d4845d37217d62d52dddaf9ae8046442b76e08275696a6ac7574c26e7937702dd07c833fb12e9c4b1b8424d3dcf72e1a0247b5f70b8d5c689543940d73f8e91a95e745040ff006dfb86fbc3a83ff62ee4099f84e5c0b272363429c54efeda5e343fb2d4624945ee62ec57d72aa7ea55493377501cf82261c7e383b75e1624a58774f6f81c032a498e48610ae7121f4d215fc41a1f97286c40213d65ecf447bc9536e9d56b628e1eb66fdad409bcf1fdb2f49db31eb4d45bd0be9921f42e9bdfec75466b361a0bacbe5618fa74f1af321aefee593bfb020e45e7db62f0426ae042ea4cdab3bfc00f8f81c737779ff4f5944afb0e913e5fce5b79b6d0891cecb8aaa96c8c22ce23af38f2ac55025e2cf40ae22bf4d7e48f478d085e4a8ab4dc30d5268d5964e1df87c3a2d9efdde9da5d0bfbf0a150206dd95c527bd597c99a8159863e437d0785b8a206f15ab68e85cda8b6e25c52a382c90380143e168c6739b899e1b4ac99eb31fa97d5add59fa829e73885c5119aaa153a0a6a09ea3051d6bbbaeb28451fcd82ce519f69cba94da94c6b0821c20e05cc1d45c1f0494c332506ac72eb9708990dbbad63ec45eceaf8c83b5d58d61c3805a726522e36ce830fffd8293dd9e9274f1e8543f8b38884c7f7ae412333712eb97ee48e652eb17e7d4c7a4382700c1c8393b6a70799d8ca5e41ccd163f44ce1be3d4037097e65f05dd639a078bb69aafc0280c513c8dc9c170ced8e83fb91de5ced41f0a07e998a61b749db9efd39483156169d4d4cfa50887b42d84363903d1b27eebb320700120eb8295010b8f1f6b77932fc0416fc87953a91046341818c2b3903070a4517ee586aa31ab9768f5c7a347c006a1f9e3fe0b201d921fe88eeece22945f61bce6adf6e2431db8abf7b58eb5c2a09c9a1a5b6a1ff910fa32fa74daa0f548384377f4b8b9b9efbbc8ecb57dc0cbd7b536ee100f5b4feb1d11283c9e635791b4be028cac2ef821f901c7a08dc40c23042f27dc5f3a4a5f7c4751e9e90c0d0a6be36c2bde385c0bd4e18b9484f3a03de830457962958f4c749b8ece14c6181621094fe21a0302b1dd5ad5653b8d043016b257108f8e9ee485241e8419e49733eb78018aff00a42890f25b5e1ec0a4d248d4f373b86753e6d2e34cd6aa3afa62645232c9e9cdd1dee6eba6f088a3cf7b048962bc510d72b52cf419a626283dc78dc2885f963fd7b5dfd0a1a317dc41800fda0826c3394a990d21746a6648acc7ac734180562e390f12e7d27594ee59f9308a3989824e0cea47a13652efa2dbc9d85c924be27e5a092816c027ff6183fd793c60cbe78897e1cfe360842bfa6c9f10fbeb8539cb524d357691ccb450a4c87366704c1d1a8016d2c2ab480ab5f77053ea023471456bbcd530d296c5d1711c9129c438989d192219a55de85ec19e1ad1b6765a32d81ed44b38742df9f3421fe37ae5bd8088703893f5536ea88ba1b5ad2d6292afc61d7bc5e9ed4d63166cdfda88ea95fad32ec1689f196f0b6802e09c7ad64eafc5758e16c24c1c4c6ee2980cb874705a061093c6ecfc97b08ce77ab11dacd1d76f7be023758b3b43a651137cf0ae903ad147218ffaa9122401f1e00253ccbbcddb04c4b7905f224a51db593ffcd50d6284612cf88ba65712a5e9be7e2cb8e5cab54f4e3b0ccf784e58462c88441b2853ef57ebbd31e962738bb59b3c4a930deb215485e7f3cdf247e412fd0f1a0d575aa71008c2cac039a380c7dd5260b5917da820dabe5ec669fd6774f4dd0881eac0944615c3d68cc8591ebbd33778d04ae3ce6f3287d3773151c1a99131c8504383e9b1f645ca4aeae5c352b589a3bae1b04185732f5bcc0e25e1d8e062c59ab85bd809116236735e751a32f1b13bb96f06c4e80cb946be5b2c43ae7711614fd5a91fd62874fa19c77e38ecdbccede308303789e1f9731547cfaebb676be004f1408de2fd436d630403d716fe7cb66ba4d52ee607d9854ae9f5a5612255e08d5a6cd5aec2b9746770ca92614c16ae1a689374f8596f87f309912d413c5f677fda6ab375672dc71901b6c77f17224d66d641d5e78342d71961753d5470d358b6ff54077f206ef1bcaef132187bd806774904e84bbdc5c8477eb4e63fb5a94673b0083bbe93145480d855af627aaec9c18dddc2eefb5d929290767785c1d37e2029dce11242f00fc8dc91d96e192c5003f622cdda2bd4e92b5c7f98d38294516b8c76ee42c190e2c5b11be12227a96fe21eac0c50e95c94cca6d4c0e42faee43cb3169b5999f9103cc2c0c2a4b25b1cbce67379f19e9c8eb05836cc3710ab54c40a784ceb3b733f3462cdc6e171e37c00ac669383bb41e0c0accf6271d85e924f5890588c09b74411da0b6bfd760b1511a67afc64c68a235a8c868d6dc6d72cee33b15e60bb655f7d364294b6080722c14d203d187a7bfb4a4eba805776ee71e787889982e43d324b8699b0781b81254f85d59570224b31a37f357b20c2ac95ab5710a37e7d47367409202d9ca6605006cb83b10edafda570e0530f266191ed26e878cfad4526bb938e4981f5145413de6d072a5c855f47171eb8c6d7be1443c9ead134ac67983c29bc29bd409a85a5d1840809ad510a7a6727d2f4c16488c78dc795314d5aa4a883503c9d0362785600fb15feb87c36e7bd6176650878b1b5b68f72db28cf6c997be4049223ecc9ee94308a086b881a11aeec49440ddfe2459622fa1cb40c3c78e8a4508dba2d5b8c1b26e32c02c33f7313d089b836e8a1f8bd7c303fd2b3520a16f3e430dbc9f0992647472b025e5c64a4571ce5b58174e6128deee01c00a9bf29980450fb8f4d898d367c805f03ba9b9367ca50239263a9e86e36df944fa884c8243cf1584a188fef0b13da3afe12b20d41c8dcffdd650c0c105381dc9e3b5072426b4bd8732b7b6c017900b3885528df6978f55b1b8fa5f93338602f36b1d392675407a3afada894c65a8c2bc858aa1f8e356752adb41f313a03a42c44cb95245c47526a0cfa983956b2df4219860a1f78c56758d5e5a2f38407588c0c0ea10d42f1355fd64c99b3872bf2008af84b2204b0ef583544643bc80a903a42a4b5e1c87569b744c694025f897c56ad177b2e31dca2cedee5d633264c21cd44501fe95aa68fd0c8674844b2da7fc72e4c2ebb6123ba7639bb0290a07718da0873bf53d59196a34b70ae350f166790dfda677783697d0b9286b6773d885696a444e1ef27fcb0467f64dae8f298888de0cfd9384f1f811f803b20b9b7e242f638706f26f988e8d0a0a64fbec4dc3b5ecf115a6fea253fe360f7b67728b5f79e207705f53f09104e6207110440880634f254252bc8be0a21946bb5d2c630ba89e0b7ae000aaed68593dc1a476f785ff8fc0241ebedc03b81a00b4df49144544e7721642efc2ec93345e2e28c8e39d62d5dc117503aa77f3f78a587ac6f6da4ba1dd04e0e1cde55a50555ddaf8043f828abb2c717607f9af679ea555ce3a9bde6faba300327c5158d56e0ea80d5e7825efb7c7f31df3fb14c4ddce125bf000826faee700ac7d85962106c4229e20b19212fa5e161ea3a04e85b3fc0b39c7ba3371fc560777597c4e94e6dc8ff5083c10dffe763daee77a5537a4872e95fa84680db5059dbdbf40939c69356fd0ec8a651b43202bdbfc9510dba844e7ea0febf1076a01051fde5601afe9719140df30ff20d63bede2059774b903c1f4764dca04b84e93ecc43ee74788a53e0296bc018544de61afb80d7b26073ff0ad608d9aff885c022d2d5973b32f152ef40625992ff98a0271905e0dee14465ede0b1b001bcdf04300d42a65b20efeb0fdd4c13d53dcd0a43aa6d08cc4c7ed270cad29c6820e6759aaa78fb17f9837b60f79b2801792908c9f7dd56acb49532a28b9bc562104b751a8c2e5baaca9d765be319459ecf8cbc4f4fe7183daa93458256a21411b10d1a2e2e2efb55b450f5613fee2d4647d22ea1761684ca9285447b3e4ae55ca1a2528415b781522e4184611562458e294f0e67945fa4697ea32916e937e28c338d9323a481138f7d96a6b71ea681750810d18350d3ff6408e264ecd5c7559f586a30fd33d14452d8696066f7d6559ff4347f4826336289a519d6f822410b260ccec584551e1f4c79628cc05ee4c52f0246255a83caad4f32ac0e147c83b9f4a85cd39abf1a2fc6ab48c81de9518808d896d0a24730f3f34754d1faa7a90fcd4e84660338681e214865a84ac391fc84dc8d6c60098f731a71422d38bfd05cb3b2c1c3504bd93f16c1b25c6a8c771ab841761016098aea7a040b9d5dba8c132cbc3f683441724ce23f68864b24dad7c8ff8de486b39921c7a052ee224206b06310e770b7caf078a8064a0782f4c7dfd40a1f2e114ecfaefcd7838f10b4b32473b5092940a823efc605a503d2a236d3586d78ff12ba5ee9c504de81143c6778e9a1bbbbc105684fa85a411d5312e9442416548df55bf78b17dbf52034c55e0557dbba554dec4c70240e0ccc02871a8a4ebdda422e2302f5b02c862960dac897a751117e4a3e8db78ff0b37253c99ec2b7ba5ea5648cc718a780d38e54370bed84381049570a8810843701677319b84895dbc1b90043b6deee48bd715707df168b451444919f26b2817640f0882a9347782232d50a286a0eeb115f80722c1cb4320e75b1759268c559a0c26b5397225e886a61b8f30bf07e468e449076eaaf4ee6e13d9cc14c98de77f52e20d2a7740df0c8b281ecd4cf8db67fe29234e371b01aaca9752129061ca2f1843fa758425aecce226bb902ff995ee1a2415877dc4cb27e0278d46e8770e803a7c9e2401301524c09428415cab317305a0f106bb184024eeeaea5f41ced6677f8a583e37c8de709035f7d17e017a3854153f4695752a81cc67f85ef7ed9ba24806a0df88fff15672494334a03e5f82bf50427aa850de4e6707bb2f9df6774f69bdba11b5a31b532c2c9b808db9e38d4ff3a8afd62af8552e25989686d34115c2cc198b2b5f620808c47946fecc59ba81337b7d304144015de3a76fc099c6a10f0122263d907f03c82582448257dfc72e7a9be526ade6841ed357997ae67eab1636feafd8d2478a3c1628e7261ede2786b53adb748eb55619039731a1d3ed6a9590d6e328acc35080b62afa582e3e8fcb02e86ba220a707db97c1e0b2d924fadcb25a3a479dd9ab616bb99c6c4fe4549537829ac5cf5feb1768634dfb97d4c3a1691b6d76f719d1ee28dcab94583e6f8984e263a87d214865acc79b8b1356195f43a2acdbc10c6523eff0ff81102ee75697097a6b91563bf741af02309d42a39bc2dac32944fd1a2114818f733e7a6bc275b21f71cc88835a7145aa5e316647f850936b8cdd2145d13fceecbf1345f07bfa8b1c0a9fed446aa8fb3926caa96b07e5c3f1b0d5ffda1bf46a7bfd959ef5dba76e54a75d9b2f8b3b407efdb91ef207eb8aed28959c5bc28a4e8fab3c33797682746c6c6dd1ae8f58c2c33f4fe63625a3470b8692d87fdb5e66c2d742975c78006737bd87cfd45ceacf88679bd69e3d6f9f767d1b473d897a9797f9044edb1e7d726b6ecb68d12292e177894c2245634688b375fd6f84deba14af24911da031341c8ed3e8db8dc76e3193af64e9703d531315e3d95bdd96b39eba1653c2d189c4903b3c6936076a39c609e939a80a804940c96a45fb72dbf37b223427a2c1b9b7ce7efbddebcc0f9c9fae71955fe0a223fb1eb34fb7dac549898bcae9a97ee066adb584f5470886068db6e03f8c1d0b04dc4072335d6e3d50c61f53510634074eb2811e9f551551f6b2e249e3152d83b2828f697dbe25c3e9d045fd2c348bd11b2be14d637d425c39b4425e1624635b5e948d4e0396a878632233835b19365cde5531fa25e6ed2b03f6400bb286c95aec26565c0f81511a516555503700f184e04bfb6ed7aeba9eef7befbea992c0e72154bc88123c0978601ddebe1cc4b54d4dfca8a35e5dbadc1307efee23b26894370b40c9223792340b25519e0ebc597c1da643a8f0b05650f1a82a0bbd9eced4f6e13ca6a52952677474b34f0dc473156d8c8b994b213edff0eb507274485089433dd001c7c2204eb45928eaf4c50fa7929da66940e4071e8bb81d7990a502f7680e67a6ddd565f4bcea0693cece3b4db18c07c43849fed65e34f3f1b8eb69f7c6fc3b872934101a3834ca7fae68f14e960454ec13b135836b00c5fee3cfc9052167ab95186993796178dbef5b30d702d42e757153bbfe0436f7abfc8e54caec1dfea02fbeddd961c5cd74c6ad0ac2e485c40832187de72fc3a102f4a26828f72aae8c282e54b09099360a2790ab098260d868d35648ef092b2d56f06f3e0b1b5d0fb9181276df1c5a5914d4e7158621e5edb16f1ecf85b5043081a04859218bb9126e2411367d529ec71d511017282474f27db8e78b220c07a4cd06264b30d3af1c8dc27bb8866cc22606120ba8fc32e0400af57695d41e1ec44b80f8a142a2e1cdaab6b894a62910361a2a0de0251bb502e4d89252efa87eefe8246312f31412dcd78ae2fe4e6115f32f49df893815d9201035fc4bad0630fad9c357984c4cc165d26d36bfcb388d1002734322d02e7e80ad21a413a19d73d1d46b1f65fe91756764b0607387085dc60f0030685a8ad85812c360344c290983e330d9308d0f1847f09204d97dc12afdbf00256003f30befd575485940b167f5811f2a43c630599fefeb6e3bc483a1872de13426d3d595c775cbd7b625b38611780856a385876db4b85a0e91368ef52ac02e65c7dab56da0a7a91616afaebe4838eb8428aead43b26589bd0d9f0bd64bea688cb2d7794cee916ca1f7ef807be93a6b29eba1e34f402efce22279513a248e263ecbe15bec014cddf4e7decbe30a9369613611167c541f9111d079e12cdbb8920f712a9f4b45c2aac67f21d713f662c59c4ac960b8c6fc2ae84125f57e832b1cce8a3de7c2c319c063bf0030c955341deb318012d3382e1aebfd01d1e2769dda8de97c318fdf8136fcf3e48479fbc7b17eb66bfe845eeab49817e050e496e014ff44949503a65513778469214a01175204d98a491241798152cc12b19a486b83bff2c26520e078b811a8435b09e391eed45858c316a29b11aa9c822d74320f5c895e99582f81a09cca446b50521c6ce5197ef57c5fa5b0592d9834c572b5bc6b3588756822ea214ec6f4a8dce59ad026651cd9dd51f91d4d6ea4fff9bd00579a1f6853a56bd2637d363e916501afc0a8de85e1cf6367264f1fe9252303c889708341764a874ba5c202d1e261b6c8a2826aa458ed7d18896ed5632760adbc9f7e8b5e570befdaea164bda37011d2676fbe0294f29a73408c4551320995ca8644ade70f0cbe72148e8a28f397ad92502f6ecacbad72462391a794fca26e4ec3e6acb3f9c27dfcf6e884eab86ed013ceb7c1d66ea64b5c4448ec0b86a9bc992c812a2f1149b78b3bca562deb029654691a9e9497a298c9a2bd65250c1780d751f177cdace995ad29621dd8b74e040d066071abbe5ae5c24a06ba282fe0904cc8cb2d966f6e5a238956f16a40ab553bab645efee57ec61929f4a31e595183edf1c5fc92c205d4675437741ef133e69e23314ddfdb5cf08b5d5afa143173c3789c8018350011649129e1d80b69ab15337e8173bd4409a978904904dda60d9488fd1c89ef55445eab4762d0b96ea028fd6626787c1c3db73bf02c11fb2eef7cfb70f27d41822f27b7c36f750e2c5049d834638ae2e90596e0b88919e318c73b61cfbe2544c58907bdf8a680593aa1ff15f60030751c43f83e601aa890e091d5469be9e12c5f3f54abab36ec57627ddc2fc97a9f3edbaa431deab958f5b01639424425d4890cf48e5f84c2110e01d2611a730183630f4b88b4d273d7dc5208854d649280982c3bdec693923990fe216ffd47e40b2531a9072a590083c6c69ba80d15698d68f5c260dfe2e3eeba5691d2644c93a7f6b183d10aca9a283d997bf4ec1e3bcca73100770323a42fd098d80b29fdb3c0e2c1f72369ace612eb144f27d122011a6f5c614f83bd3f775caf8846d4f737285ccc396265e3d03a604f183ead662b27e03509bca0b5054e8bec45c369b4b55a1600bd632b4d3ad32123df515f5dc7316e0f51ef8524595df45c8242af19809616c7ce1cf97bb83a7fc43b3cd68324cf0fdd2fbb80b5273a61e9895ba1a17d019db85f572a61c30bab3c2fd795763c8f3b803db8c46e3c39d90c4db1b45be21ff92dc947a1c3434978744bb89772c84ec6e91af8c48732f6a2ab1522c57d6351d4515a088a9d9fddf6c1731d7a7537b7e42226f8ee78f8d0c69c9411846fedea28af44188c2a4ff57f6aed7d4e53820fc3f349acc2007da636a4415c0db71202bbce46e23b89a237659c758a856ba2d2f676a47869390dbe244dc3feff92e8fb9932e5867ed8492d9d7d3f64984492b6a93e167f8108dcfa872ef7b7689cfb6a41a1d393d8ffad8c2ddc7b87c9db074d21c66bfb3eac740a3da145c639d83218372cc8e486e83471182102200349cdbb90b41f49ae500c1401a6e08ab20ce35cf0e240c97e65441f4fd9dd7c9f290ca6f06c6c0b66cfd6f8d979337a42c729e08a4fa8de5fff76fce82d31855f0c531394c524955b5678b7e21caaaaea6944014d4d92e5ca72a331a9163e40041c346b415ee7827678be229d4e8c95762a90c2a3a7001451eb03021d741c4e94af0d5d897e14d7a41f719b7e96465865764d2088ef9c8fd0bf88676365c9a895854d1b7a2b188fe7cc255d83bd72f0c8e038a11147af36984a1315ea397a60f8543a01006a368eec8fe15556e1676161f51e0203e8c4380c45a90bd8c44b7aab34181b32a959b0deffe4df3265e8d7aa687f810ffb5b43e6766f787b5cdccf7b7a4fdf303e106e1f1da99194b7173e266c93d4a8dd2330bb89bbb5a0587c864ffe9f79d27307470e9bc0171cc8e702171bd982a13c245efd1ae88a2e2052f25b960d039d8a449d7c7e5874a099a7cdf8f3f9ac603da1e68933a7eb224722c6ad978ad5a5d4cfd69094aab476bc67bf3f458b5f17dc40b413d17a4217d352840aafce694838b86281a2e74db87c9bc89814d7d4449b16ff2100a34f805158ab3f12d0965f50ebd10d711779b3485746d29311ca91f8ff6e085a70d5cb98e0cc31bf0a4f7f506bb8fcbe3fa8e0337a43538c086608fadc19f9ee14bffcd9152e6c4c7b2b017bd849103a127bbde3072362adc2cc996f072c2d838fba8856f4e948862ab3933ab2cf401ec078895ac00b3fb20bdf3b13276fe9ae0460b3a97f8bffc9298d7cba45f08eac078830259469a8002922b0f20641c59232f6a11d4be527c1e695609e9948af18904107925e39a5245e3c2cc9ecdcf342ecb1be0aa83472dfab615d4e0ba21816d9d55cb7ec1ebee5e37e7ee0fb303ce21e910a219fdd45f7621f5605581cbffe8cf84959b2da4a30741f8f085ced654277a409be4cfd1daf26d53379f4c09680a6d122e47926c403fa2c4ffeab23dc422cc562e253231c5248d7f5253a4dcd888ba24cc6a4187248e903a32218613aa2d6fad81722d4c12ed91da9ae7a3a90f9e58f30e0206cc191338f873d9387dc670461c536bb183f9af927cd91754d7a3abe6c1ee987001aca1425e786331993ba339419d724feaa507b258e19600bcf82fa309cb5bd6e1026b137976f11c89615308d247c0815c2511ec49aca7183cd0b1faac508c0c78e43604f57c5bdede68924015484e1188aa5aeb795d76dab9f544d04ab6a7b61eea4e1721d29a8b5d5fab590352eecc479392b9f3027089c620ac70d397f5b144b130f1f80c084e8a15c07e2ebbaa2b067defdafa44840f734eda233bac36c181dc5219648c29f81829f7ecea48a25b6d09969ec84907ef32e2f05e7ee763fc138c37ee586592cef238cffc69c0f807d0bf1a6c9049cf75ee077d567261ee5ba22348043035c095960b23ecc8142ec102b379cbca5f2588dd6dda28fa23befe0582999a72b9f73147d7e4b4260b30f5d06132b233a424520d81628000f195f2dcc793db3b12b05304e574500b09f4fa5f072761f50b9b2a8ec3d375d4889a0c647221d0975dd0a5e9fdfa64b181951839745a2c3e4c064d80eeb81b6d359f8b24250496e3b169f1e69486b953f7725448918ed3e3daf71972d80e543d2cbaaf834c24b6f604671732a1d86140c732b2159565a0b36c63d3aff19f5c11e666e5169175e6041833f9a6bb2349be120683b693488197ac61962a07495db27b775061487b60558e59ff6b5e8ce12e005eaec4319e656a19f4ee2884ca03ac6a20f6626332c192c71fd08264808b170431b1a21ac42e87217da941b620609e7bc81068c2a0ff6376a4429e00ea11a66f06594a240c4c3a307b2f2326c97adbdfec5b3eb5f530d7072ad94cb2b3432876e190d3c3b67b91c97c0be2533e78d2144e779531aa95fd4075f7a1d4ab37c4a713f4a80e84c912517b4a911525b000c8e6ee012cf931a6d003b78c01cbf8292d1520cbb4148c8edd9b6884f36611a59655faaf24fea1c171af7382297e05b3ab50472ee22c472f9135f1793c324bc3b963405e4249b23e2105cd39eb85c78b6f8e0ec96ed7182951feb08607a7143ccba4d11ffda940ad84a12acad68ceed207999373877780874948441e58b28eef5cde1e4a10db97718d1623819dd17d802b621c84908e95f1da4c7e1486dbd7c537b34f0dcc7c4b1fe0e2199fb74ece00e9798e87b35469a14d3e487d7eb34f642634384a9095b8bb4bef47cfdb363ea2717e52d76f4c8cf929c24cb76b1d9f22346628bfb324805e0977bdf3e401da7ba36f839988d3bb3245905f04923186eda5e998ee1faaf64484ca62576df4e568e3024cd15ef022fe9b9d2c0fe8c834f72795efb80c4957af771ad58e5d98f85b16966518b95ed5f88da2e0931f86a5c194efedd2751aff7923c7638e75b30aae9a14cc7a95e8c1a613c9f4c49c5e49abdb1f283c2127478b9c43ac74b1d0fe270c4b525d9648251895779f7f35b22a38c3346c8f74213ea520af686c979b1ef287239701b2c9089b7a29a53363d460e8a670f6995c44b0baee7652582f5c34b3757c2c8a4985b8f8cd84d641a0a8059c4c99ec5527ffb801432613c4ec8048a41264e1d297732278f28d31912804ce48e9af36fd090687719ca0333ddd16bef675b661cfa1922b918249c0c8026f859b3c9de0cef7f7d783a7fe5a59e03f37aeaf8780558b24c68b83c2aca295d9f7425d748441b91532dda9c11e0bd2fcf01eabc24ed6dbb34c06039b75377010d08309c456bd19840d132cfe782e568b428e79e04a98be8e7268fe782d295a110ceca78a0f0b7e294c2c3c1a7781a0e6324c4971eb8f74e4a2e15e8ebd0f6e2b7836a35c820497968b36954268cd8ec45c9c97cbedbf6a8ebc51d247ecf133d1db496e9135e8332302741e2adfebde882c7a12c86b3953336458b438ba8c85d8ef6d2c626b882f955ea20b712d3c7feb0353e9c65116789f17d3d75a3d17185e9e0f2dde846f217998dd96ada2079dc9a98c705ffc201136fee3cde5d1233d0d573a1d9064031f242a9fb97fc0ea8e96afc43297318b7a1871f04e50f22e0657d24ddd2020bc4450807c0faada72edc2afa3f0ffe376d6b828d68bba5e310a8edda37fd02aa5277483c95ba041a2e1866b6984bc0e6e90fa5189cc5c710f3f55bb83dac52cb6ac5661a98b8affa4257598e7eda68687cb13434b3341cee0411cd51ba33875f6dbccc3e5c5d4ce955cbe1e49897dac6accdefaa5e5e998e8a358bd11796780bed736a0075c689c28a0f1468727489e176df8faab503bff6d1e79ce719649a028e0955447c3d3d06703a1d4050594d98478546a73fde642c81d3345c9f1ba91f8829d469362785a79ea4264f7551908314420ea22e61b3b501af02e561e1bf5cfed11eb49efec51ee07fb43fec4a060493014cdc3fcd15296bf881a11e5537646d0e2a74c4d4800e08716d6a0d674e5aa1355da8094ef2fdcaebc4af35ed0c1bf566b1c018571f18a5b82d5311f51b7883ff0d27df2e0ec21a89ec61518a442e329139fb082e2e1498e45795183bbc762cebcf1d11aba08438c4fff6ec8d6912f4b30feeda9549cb3e348a5919ed38e23b2182866b001f720b3114b2de81e631e9862bd3af6d40c99aa8ce74e38b4d949aae0fedddf1192e3d69ea3348355d257f8ffdf8c493121017589db77ad2c3b168e2c2c493901117acfeddc101598d788ff16a1062654d3cf95d7e87cb801a0a5617cc358ef029712449ba6c4b8dcf84df8c171eddf1cdc4d8f5b27b1b7550e006e1a56dd8679c9846aee63082970e782378d9cba825b8e9817844037011472678d00339dc802c4990ebec84b624a42131e11d34800bd60a78aab9790d165d469ba78ab4e7d795456ea7864418fd195783464dbc73c0801fef5674bcfe17e616af9674851c8ca1b88a67ecb545a60145553920eec380f62ca0c40c39d6999fb6ac240dae3ba4cdf776795e0ec4d503a147f8aa2f781d1805ada39566a10cfa89e7c47cb01bdfa13fd1176e4a898a8ee49df44557fce3209a24f3248cdae83f3e7a6531c3047d8717ba1f0946f1edc8d3f123eef8fd1635cf75f800c6e0229bce2a280577c840bba1e71668082895ae7edab4c73077a2b28012ea01772dea1cea850a2afa3d4685bf50e3faa93c089a1e2b007759358234faeada36427d3ded98747bb75e7983b61e70c7cc6a8e6a4c58f738fb5f068a24ea2de04a87fdc4ae2bf487163d4c39c9e96bfba92703634185327e19225bf38fb0fd7d1da3e9a083bc39dbe7fccd3dd9c017b7c03bed46f7377799307069dbf76b7c62ec6ae920f915a862b8ce07f483637000dd2aecdf9abe681fa49d49f26e4d93d675c27a3c787a5ef88d6196dd7f15d7a876681f5c7b004d8176e59b4c0078f507b05e40e3c8012939f8063a3d0607948a3b41d0f09709a0d090e92b08272d9471af25b8ee26ac8be814b9da1da32e9120b41888895569118b60ce49a3fbb2a5f1e3af8b9231ebe40ba397bd5e8355fa1261be6ef9daa8fc3bcf00cf58cdc32eeaab007338083c6e4f216002538dcefe065450bb5086b25d40e8986240b88bce6d9ca2f0b82d4d90a38d355eb3cc2100374b48a905f78bd4029d1f6ea0384478ca294524da9cc5902e22dcb75f907e84a1284976fd6a9f07ff1500f234fc169dc1c3314ca012e05b0e201eaf8a2e4e5088a47ec24651eafbc2b48a4763c8b7947c0d604456ca70a308a5796673c4279bf8ec8c86619529289e87938d568d14034b23c0464cd15d4f8959cf2d63255d74a008d444b70ad136fe1eecb8c9c8c43e4fc2e5fe56593972b1efa39469a3cdbaff908de605ae7458ad3e94aa8ce85c7f767f848b2290681122398abb84471a9044374d384bd540faf626ac4b547ae0f8e39eb1667b80b100b6f76213f68c7eae908773c0c3dafdf2d8d708cf2f34ea93ce512aef77e6e702993f6ebdcbbb291ef8b499e0278e9772c6c0a193f917f926fec1d7d62dddf7a22a81e54257a6fd9566f479d3ad264b744589687eb295cc89c38685ceb1fef8db5eb61686cdcf107dc13c32ca234af58d97753a7d201c91e9fcfaacf8e0ca60c620de68577c95426f46eef0372a125c9c8829172f1165b6117a0ee8e1d3e7aff791c7e1dce0a553c7e0fe4a0e71dde8218686d277937eebb1f394d25a7bc660ccaf1a8112de3f71255655bd7f1811e2484e6cced8a717c10ba2a1976c9526233efc3d080b091293b083efb6b0b7d04c54dbfeaa9eff9ade1bff49b1daa8fd9b09803de69fbe5abe21531f2c909bcdfe09474d39342a76c3acfa507a113b4345b6bd54e6d48a0f375f4f1ba8840dd6ce4224a28dcf6bbc50e88b85888feace22a20654c0ece3028aae8d3d6920284c2aa335e1e5e0969260ff86bd710967eff71e45ed1851a514e8fa595446ea654032504a106345d97ad12efe7764819e654ca5f4ddad868a3d098734527e06148d48acf5cadab2b6aa4444574542ae1449b00ab8b6e4220de8347680b3d8363368051178ece1e6e35f40e550ce3dfe78d9b19c8df3151f74b388044ca1aa0c951f4b1ec713d08ad0154e8672fcdb0f5ca0b81b23b7a1064f90e88ff9765a7289b48050f6458c09d1373aad91510b38b09ae2ac58c423af53ff698dd0cbf75acce0e3d14d8ab6d19fec1c92d2635666748f91fd9a1fd0c1454d5a0821c1b2eee040f91c22a610d98182bb80207bc2cb6e5ea108b8b44f020131e269942e2dbaf24a159f6d2f0b49ca8eb82ee227d8d15c9a1a3ab20d2dcb4feb5b2233c2af8b9ab555c733976666fc059b1fb39515a75a26c1d272ee5d173411a723b2e0a4b16fb5aabb656b4752f7705971d52296b9400c07908c01406bda1b4e5e21630a4631e71cca55ad9d35bb5a4cd41ac4774bce8ca76b08ebe2a455b1161719e292f945ac8caf54e0ec0aabe57bf9c74cceda24b0753034dc560ad11d9691b1cf9c86a9f3f3153f99092d77e9f1a880dad8799daa172199685322cc8ffc2bba2115ef7ee7adaa094e4f2fbc9b70a6de4e3d8f48fc3f8bdae12ef88664f710a436de7bcfd0bffc58863873205f25c225f5c5120334d08236c85aad2cdac2f01342b0ad4e250fa78fb0d6c3fd7f3e08d1c1e785b38952e6919bacdec4c490dd8052246b6da0387e8db2c600d2ee8c5a6740bd60ba3c89306ff46d4c497aebb64fd0018e7d94e153e013d5181727db668db8b87e90db063a251b61039bb4ed96612766742eef645af236b693b1a3af31168784e0d1e7fc2ad9de8e9c73c9cf907e6a6f38c3324cffbe036dea81704acd8339ed6664a4146fc309dd2c26c208ca2451910e8b759ff4d1b2caa4f3d04a53ee2608537953b626419e55c82443e1340b43f943dc2b0a35e94aa3b0193341cfbaaf310ff55735cee8f43aa7bf348820b264a123e9fed8d01b3317068c8a110bf7281f6081fadb4f5d1d676a983d455ea9c1cd1ff22bc60bf94826c89c45c0a7bfc47b7300d62d5d2b259d1fbd33728cbe75a0f404ee00e8a89b7e13c380abef06ecb618c97d589155aca75e1e590389fdc751d68ca534a4b5fbcb46ec60f7d2ef9575557452614c3e85cde9840c6686e938c842be404f5d413141c382c5c1506d16fe4e8e0bdec126c906d9373291db57e3678bc0021340084fb001c11370e6cd631b9f8e465b380e1efeed31db0cb82e77ee63f812b8e3063d88821ff21fef937b83388a939524bd5ff0d718c8ba5eac2e6352bab44b6550522e4892369ed32b0f82fcb2885085f322c921827ae294ce282b9a1f7422f670f6e5c20173109fe7ff9d55f596412d74452b794f365454722b0941d90f182bbc025140e00c4715692560644c480ebb69e1ff38b8b8abcdaeac5d1ed6340aea581e0ade3f2f3721ce015741b3696fc90caaa8f16025021252e72c544b4f244a50aac5956bd25cb197b6a64423ad3fdbbea6ea4558a91e6e9ad0c53580ac05bf8c27f441455789bbcc6bec039bc8c262ce6f462fee7813b181f4175ef0ec247424591536a2497d48df94198e1c6bd4b771a6f1ae7fc9aad425df187b32c00d45cf04928199fe94aa25b6a0b01c81301737f1041b0d6a3905604b81521fd79041c6cb174a321cde00153732f461adb08d17875e10ba2003042a13ec1f6b1073932210e28843a396baa979652bcf2ac10551759f30be1cd6b5ccd4187af289280bd39773566e04d901db92ea45292afbb9ac0d118831fb098848433cc2872a066fdeee7e492271f6522d9156b7a6d867a04690c83d531724ef8157b866c0b6c32b0a87466d2e7bd1a5db798db3bd242f19b7e2c3fa97c1a84e93a1f2896902c1ae9a1ff45609e269edb0c5fb7d9d454449435c6a2724c70a1ad17e49d787015ef30dc8afb8f8386cb324714c2fa62afead58d41515a0e1081533ed3d9c201f345a0342c473cda430dda7a1bf6d074c4e18945cab26f4f5064ee861eb387af3b4f0bf00e46c01681f418fe4ef9a674b1af2e33f9d3631e486b419d771b4518a8ef554b8daadcf1d6dc89387a956aae35fb70b7264aea5d3f4a7976fd4ea4c0135fa1384145d725bc54328cec3fb3012b92c2dbc924ef49dca8fb0cd8f5aec2c3fc2458d90623cfa64e5d80a3e672af682738b496d621d88d6fe90ab98b2a67380fbf94d89b183adce7759f0b9fc8f9b4b033775f614d39c9842268283c5d9a8c72f727cad9e75debd083ee078e2299bd06db9d7cbeb9fd956f6fa788f3117fd03efd2ab1ea9318a1cc763c15f1e451cebd19bda27e2b13cccd9c03617c3507d92c755fb69648051aa54124ce600fbb17eeca71fd3e0a9b7b1a8a8d07ebc361070a000269378d709285f475407d5f650c2a55b52cc88ec033952c7f9738c52e48cd99567e76fbd763458e1bf4e3f49f171216b10741ceb0f8ec102980a33667c54696a4bccd15dfb44f5bc7b17f9f721a4e1c73e8f7b2a3d42b39c34138106579eaeda974294b2089f5c35ca74968307112073abc16cebe501111bd44c50278e29d0fe16517befdccc0144630377e369be89501e0183bfb51f4bf2482bcd077f0b7b699816ce3f2a55cd1df4bb828f237b9e53d59ad58ab6601ab5f09e7f7c4cb460d6efe0eda6776d5f2adbe8529f617e3ac31a43e6dfd7f69b90f9f10d9dec62307f5d92de16ce1034c446dc2c31408687cf46629a29ae9e2f3b9a7e6950a625cfac5cc062914bcc911cd8bdf202bd2097b64f575a71129980029d42e81f3706f06293619713b631e270bc2919f3e46ebcdb3eecf519e6bafd8b685c6d861d9320aff5359fcee1cc3dc5f922f08bacc274a0c092bcea26c5986b32fb7e39f66f9c58e59070784c15f08387780b183ef648eed74abbfd732e463e7e4f073870aaab0d6cd5b6c45a5d8cfea57a3160720cee1b481ad4b8330f7a15ede0cf3f4feb4383fd044d5c0fea1a6c865741d0c82a78950e786d7251f0930923090c1a70b79c29a01136f3d2f78b67e6ded29c42af5f90b210a75a5d4ed30ade0841384d27400082c72585bba35d4387e0fb1eb512cb483c8a58fa970227127cf274b2a924a01d38b6dd24eb528f2a88785712662e493e398ae18009bb267cd6d50b5eed0120445e1857632df158af32447ea1dab1b3a9de1f39cb1bbcd8d0a912514fef48fabc584a9e70b62c4959434ae98333045f194093e9b4978e5c283cae849071905d9f2552cd9374c460b42117ed74b991686d306213c7aba098818df6e92e281cfea8d0809475fb5186f52358e10fbf20f711f6f6f4fcabd3c55aa703276c09fa05238203821dd466053264f93182a1e29763c603706b03a8960e8e88a6c1ec5edbab34417d8f3a0d0738a187df0243eb089b63f1ef4dee9db01008fd67568a91bede59496709dc7f7aff4f223ed78d1fc5c54da89c18b2a18e4fe36ac90bc868b900975fd8ea40717dd78a48f48e5c4ca6db3b7f0c0d122ed9d38b992279bdd569eee7feb9403c5582bcb011baeb1bb8e464172e730215c1dfd8b72c51e2f786e325bb470e3246b818a51d16786480e4c85ec938703b742a97921681464771f0070d49dc6f6d27f9de6909e6901c5ce0624641006e57196c3a83690c40dbe29c74354b2be41355ea2830f8a37fe25736c8d69ae34a116150e5f4f3a398037d045236d5e1a4811becbe941b39e8e3bb659991c7e81cae057b8792566a751a7b546ebe194d4843c789d9c268f8768b7a01505c178c332e110295839e2fb0229fc9458c6b219bcfc07a24171f04f9f3f989f249447ea19fbcb1318fd74ff1c49274e92793ba6774975ef9c59385760210ade13080d9d009662c7a41d170f3fd298d712b32e544599b77b098ecf4f40efa029866713b8fcca06c629b813d5186b074b951a1afbc4f914f2e64c75d7fa6fc7bd3f185c908c68a260cc78d0fd507a6872b24d0deca6543d733c63a6b31121b02ccc4ddbc9ccb0aefb84a7c15ebd99b00150b7d3390745c3a7d399705c1803618728dec8c83ebac3142ffe1e994481928de878b93f781a9080c999a50440ca080b8f8758039e62c45a0b02403463763268529defab3bd5ab5f77ccc17a6c7530e6514bf0c697fe79f5b496a3b7b3da49dd5e35453417f615f5fb0fffca50e9ca1676f9ca60b06702581e0e2ff797e4e60e25175213c13df17a7151fcb7e7156e5a9ecba9d0a4b29d01ff3cbbd8e61e14d62193d5813cba6e3c09102891e151fdbaf857010d355cf5d4ec70853af188a6a9c39bdc7c4b3100fba53fe6da8f40f0942633a657e74ed3f633b67e4e9cb91cf44b9bd0966803f283913f33b3eb9b4d6509ff0ef65653bf5744ea2ff16a6deb53503a7bdb8e1f62f42c2bfddb86eade0e663b96ff44493d34d57dbbe9d44fcd8f98e766164647ccb444a44f14da7066a53d8c9bdab1352385dd91bcf7df835108f7c9ec18b0bd1b0b75e88b8f710f71def527ef31c2ac36ca0825704bc63fe5632008f6ac078a45a75cc6c180b33ed027cef0030f7279d978df3ad913f06f4c3ac54dc7caa02d6c00cbf5b11ac67b6911f5adf2b61f9428345a7bee5c390a3c362a84f6dade6dde877e69043f0f43dea60e25a80b628d9ae25850f0977fee1bdf660f0548e18c3371d4964003923515628aa355ef916b3ce5b6535ce0ebfe3103b8edd7bb0e4de746ea479e0cb9cf6ddd37ecdb4a870077370cc5dbb90f14527c811a51c9beff5283b30b81aa7f19ab0a578151c3638f72ba950e4c0d87351f37e98e2358d917cfdc32925970439ab61df1830c130076a5ad3586458130884c937cd93a421d8bf1092fa89635f11123cc118bfbb2f7abdbff8187967ed3daa70f0ef99bf02a06355e49724c53cf98d91f044da27e1c32901cfca402a4b7a30e26009e912a38f2d28b5384cb373535395b99e1b602eb8cb36c1a913d2207b9c7a957dbf279ff28c5dcc353b93c75ae57e9ad4f05d7f41ce5cb653e9d2b2aead78561dd6278e029c646c767ce6f3f4dd5a169c89a2a1ae3e01748d22119a4bac914c818bb6bf08329aadb919e544e18027667d01a080f802c914bab87846fa47181435de01ffc729d4ddce0e9772ade3b50adfa0c4cbd3bcd06bf9f4084a5cf453eb786e8397eac67b255a99cf89fc29acff897ba4602eb43009c7d4d31b9f938d5a9002881fdd8f7eda344392163b32a2f3805a075f4641b9730ba99718eb036d0e26694deb7a77a01cf721599739b7a509abf1b31bb09307245cc552e0e706e20177e84382742b82faed6896ed95cf46e797d02f37b0cdf2c3f65c2594001c1404e5f00c9b8f7ada00129570d53dbf93e3bed946100310f507af0d4e8e68bf543e58be6b1b00722ef39e51dab53f7851b803a1839bf81ae3e8e3630140bd8ee08dff49e6c3af2d89a86117b668ce10f543dc9e19c4b6a93139045619e36bf6d6b091152844a58c427cb0c17feeb0e723e6831ceec3309c9a1f3525945d89e2e87e5287ee9602221ae55143d9689db7c6d3e01f4bffc4210260a1127b18a15afa57301229fb78e50b0210ea0094900533045c11fbfa29a0fda065f54ce88270a00c27f970c1d4a638944a0f3aa269d792005222509e10a059069726db11be132d76df393efe38d0c17a7f3d7cd32b03c7ba5f0144b991dbe0c10d5dbb6cb22a293e29c3eb967a616c3ba827909034442f4134c125a92f38928a9691513e425036be38a545e6ee19c6ae091a24da32ed88e578131e4b6fa787f98d628fc46536dd84bd13b25d624aaea0f64575d4c90b3f881d09d3e8d16f67383b0dc374310598e2b4e68add8eb1738d6620651c7fc9c9b9ab2b8eeae4f5913e495780d7771763ddd3e574ee2456c23d9ee4a97871d095a1ab84e39c5e87e90e8592524950d7c64223104ea4ff458c8780c712245ced0c9a225bf9886486c920040f172bebe25193b96bca78deb6fbb2e91ae5cfe1f0358caf1df8c531c4fe70c008002c663ea71908f6b01c3e8c1343c533c2637ccb2e31b6a639a9c7a13433225672d15ce48d953e8426213e18a5b8ca1b21c5f96f7c7c403e55fa754893a9d44b2ac3a4a881aa9fec129f2cc16ae302ce7caeec72004b3be6fb35d7e5af552e49002149bdabebd7355e05c5e9f66bed8255a0560fc4fc6bf5dfb32cb7d2e822429a0a43c6dbca222363d986c5004d4eedc9ecb9e8a72c65f318a22c62e3746a2357ea2d02f52bdca1137988b80b3e507599277a8abba4279ae1c54533ef95fff2fe78cada979b1015b1fb936844091125f5ef0486a81a80b4cdaa6f9aed9fee0d88f037da901c4b44bd6a1973248bf83024ea59e9c2f367bb51c86e9129ac3d93b5699a9b30465941525bef5ac5057888d988cd93734d8199305ff3390a508804340fcdf69191041f02cf3dd43060685f54c4112dd995d6bc8ad6b988cd44f449705a755ea6e87210c88df4dd03a592fbcc516c27318e32b8c108e1c26f0075eb2e31fa3af36923abf13951c20e15601f167dacb748b4bcecfdb0f7be1fe4af430eadad7e10c293ab621ba633bb216e5a74375686ad689a9c6a300f7863605ecda713dd1f633425ea0f1e5e33ecea7308969fa012c00c617ffd007faae06c50b0a1fdec08fdc05e2d45ced12691aaef971e12732f643a6e8a9e290d7167363ee3b30f2fb29ad03f2cace19b6981ec8682d326c875c71b4a14c225f1a98be3a609a7d37d52b0f5b9207f3794a8336c194ae465fb97d903cb07d2fffd2cae528f881aea04065c83d28d6f37c3cd2e79c9c38cb20cc283d2b1c264e7c9301bbad440bfcb801582320497c020d88dbf480fa08998abb78842711c169a2290813652e52183b411c7af822529225aa5a864229b7acc7dcf823a1f1c3f090807396cd74da7527c2b66d5c074d2f81fa95a9ae231d05d6b4bdaec2c72cc57a9cd8cae90863db11457db4f07903280f9f76a8fcc01299d830881f7ccd4cb5939f5b977e108fe89c8ac07d054f59722ff5d5b02f79c9a3aed4240e36bfd008937830e8798c89449c718451a6d452ab953a009cb7839f8e42214c91e8f7fc043a3da5e10f42607fcc26d1b92e60d1992b940112a9181dea2b46a6434cd7624318d313e6af0632030f6e67dbfaeaaf5fc48778e8c189fa879e257e17f3d444346f55dc2c8e53a6b269fb8c3723b542e59a7e218bc010790c9c2d240972a804383f24986695e95276045370c106bcf50cdea0c3f142bf63c0d7de2f345c2e88770e936c7c492c1e06232a4ee06842d393efbfbfb53e83edcc9cd09cbf45715f3183678b9cdfa0fde0767f3282225a7ebc079c34979970754fe31cb5d4bb276574692db260cb03c0033898eb285c9f80de7ee41b62dac2764a0890aac4b03344d524eee1e454ab10dddaa184a37be408c96675ab60e6132a0bd875e4a2cee04a8cd8150264da3cad1eade18fc844ac3d646938af3998aa1d931647c8a085c4d7eace76421f61c758770893d871b26443556b7cf27fb528ebd938883bfcbbd7b038d05bfc28f18905230f27630252714df369ae55b98d488fc792812ccbf8b4cec4b9f7d2311a27150e104bbc62139b08114e500117d56d72684e3e7ffca5a1adc903ff621a61295b30f4b99fba9c706aee725735c704b2fdef564ece9939f6ad3701abe3bc9c55642c58107c19bf7625071298c161d749af99bec60fabfdd60e90216c22e51e008ce0b0c2f158cf38660d7a9b470ce3d18851ed3fa2161b70b49ab047f1af704a9eb22fb10189116f4165d9df3ebf7ec3519c6224afe30fa423c95d1552432316a688f338c3da500891ce64179c24dc1f4dc8d9e8c8527985ba37be8121ebe161fcb9be4c4d33eead0871201d7054bd4fcbbeecc2a686da184c509fc4a3dee2fbb229b5f4d0bf4fbecad7812ca6786e6fc9cb069b794d0af441c31349e5e4e4808cf844e68dd7e9201e6ebc6351abae7bacbf147964cf0206d2e6f7684978bf1599a4ccc2a95667289810206f8ea7a798f1d63c4bc05acaed996d1dd13a7dc01db1fe1113f7fd6933f0be28bbc64d1623ecc75c251200b7d4b3f099f20079459e3c811199846c2e04334f1baf2b851d5b85397a417d3f912c5cbd79239e2a940fbe9187b121af2a271a0fd619fe11ca70da8875578c4cd5b4c43a1080785020d990116269a286a40b32e3c8ff23069f2458a7c98c840bd870bee9cfff318c058103d45af857b7013d86376c40285ce6ba9013630d1548c3df76de4041240db7f230a5476f49d20ac4874903aaec6615dc0eb9018409874521175c561583b3f09bdeac4225980e8a16cdf0a30b2b8a59efdc2acc81e388dd46ddc3a3f9ed18e7140e9b59209c364b131489fb2dd7d5876d4734d1e6c7606e012cb880a6b52fb8e74d388f4d19686e03a9c6a000683bb0e9d93d0cbd9dfd5ed4069740accac76145e4683bc4d0da24cff5d8e999645a7dea2460d7b334dae47a67980fae6127286cb6c62160c00d5c45655c002bc977c17e60e24d269091400a9a0286c8561dfc879fb890157f80e3828f8966481162b4b9522af29f45b4666c421bf60bcd10a1bf334cb1a6bcbd4cd3fe96ca9740302a1813a60568855ce0d49af63b038689d7d73dfa7b43449e0d23df684c10cd8d8f5195b8439f376519cbe039f1dc723506d1d95085a002f213d051d669ed403d4ddf215f44733fbb942c689f555796290c1eb1597b0e83d3c29c3b697e8c001e9c9f9dd280e6b0b266db9ff0fd02a2c1045fc97e60ac45bb2a048bda935b3ead285d3281c58db68540b8c7c09c406d090613bc65fddf1ef858e6850a457d65b3e62cf86119a5c88371b3ae529d55f09dae156735757b282083411e644c91cc8f7884521e8e0e53c7c751bb271a1427da22df2fe082d1a566f4387c199ae442a6f47ff489caed108dbd9c1946cf0d209e5e24865732666521d6c7ce41da45708ac98493260d93e6811c17d1d6240a9c363259f2c6875f03b23c22601b652ef61020065a2616eb05fc43e41bc44f3b62b4e1452902684cea9e7ee6a2c96449772af2888476dae4167f3790449db107f5a304afbbfc51847c0e93155344da5b07bccdacd91dc64f8d3c15e1644124bf709b24db72508bd107668f708a49c784047b20bb7ac09dca89d228b38562303bf91892de1374d08c1afe6fb6e450ac789767cc706b81f7ccecbc92f13a7a2d928eb4f27d36ae9b82a60958fe6e3d6c88c82a4ec9b8f48b848d7591b2a3c8e70a711d982488c66ed4314d64a94be3130a957b7ee3dac8b64f6f31374e2fe80cb46475f970ef7fa1f94247e285f8e278c2a9e786b5c8a8560087935d8fda225e2f49a6fe3e4dc3081050e5b8ffad814efd2a4df54617f9010d254ab37eab8e8960ba4ea49b9c3031a32899ac86e1e5d6da55fad3a2f0f4fdcd8bbc2f349609ec82f4ea875d0476a7288e83239d9cc961695a7008dcad5a92d09b03e3e358d31bfd355b58dcbb99376050ee169b87494063280412e5a458619804b9a04aee3eca07fe1c93a70555f594f34f4ecd01d6a2f0015e7ad80752d17709105e7a4972ce1c1f269c57beb271e3f1ce22467e9ec31e93981b993b6b649338f62b993551a8ba833be7598e77d137aeebca1c54d7b50cedf260312d56111194405d5aad7e4cdf08c5603c054e5235491364aa6b8d8eb02013339649784fba687faf7ddc84f5e47acc1b34930ca38a964d9b23fb8787268b1b48e8eba33dd9b236641091c695e9ba0597856c33a2f652eb9db010e00f6872d1d05ae86b2aabd7c520f7beffae843ff58f50daef662b047dd629fb3a3d1a48703c6f1555fb2d8c49dcc0eb452a65e5e131d4da778895af986cd27ed46ed2e90f448378dcc0827c533b7b14e3393301253dc1be176323820fde6e54d96e72c8779fdd15c2765aa8d2cd635bf21e87083e9ee76b82131f5f3e03cd500b397c240d5e363c3a5afbc7cbb27a8c0bfc23b63124f825839cc48bb54368023dc316689ef78359e6c086dbb4330acce28f30192aa66e30e16650f8aab08a319f3080afb0e4bb580439270e434438a804305255ae6f3b8981b3b8c2ab57aeac693dc63b42bc83ae6dc639f16112a4c7d9deaac6adc913ac5d4a591fb43f32112df83cf0fe849c8ff65f7afdf6fe697d65088071ddcc922d6633cc28d0c363a7ea6c8305ed6991885c8509207190234448baa270705a5a2b0e3581648f7346a5c6988b42dd59568116b13d3ad3244da4275a72444d4c8e96e29c4ea0ce926b510513470cbfc662530d58dba42402af5e033690885dedb270e4a5f501ec447c40a8a6f1dd025a2900d076947dd3a6c78e2a3f704411277e2787e1e038f3a7f4a267f64ca55bcd040274f5ffeed125e011413feec35a196a1eb0af94261aa50ba4db8381e1bca8be9f439c6c189ece18973f77070bf2a9368de35f45f971efefabd2d961dd4b601be969cf881e9484cf806e741f3da7cf3840ac56b1f2911b6954bb92aaa7c52eb1116145f62e09d8f78b7bf4b04eb2bf1e1364b3059feb0120fbc55cf49fe2921edce2a102afa12d90ac5198c36fba21afb10f6809f38ba0942b244c196620b4ce9ad685b2349f012b093edfda4e60fb07f899cd94ed0c939877c142299faa7bb2e0124e4b47f0aae31edccac30b99babf1dac961299e1e3bd71454d3f104c6d45ec03e682984e0cef92bb25d633d15e57282cf6124ad4b656a4ddcf4ca3f2a51880d38454678f213c4c8c4a6c45d51bc205a965a8923a8081a5aa97a2217c0ddae4b83b40b28b930a7abbfb70ded81f125d61009e3e76555fb0fe448de680d19ce8fb0a017a78d25908a76c7cef45412041be44113c1a746a7c28f680f91bbca13584b830c6f1873ea19e468980d7243f0f582be93404d13c9f0eccc89be85d29a870a24078442badafcbba90909530391b4107f271556148de34bdd516dc559b97a9e725160eb2ac993d8f76d53c5fd4788c3ccf6ccebef253f7f1fe2a4bc6ffeab5d26a6d38c0f48109240caf483325093f56cd9b060edf43eb437261b1648a62a036f275c88e80bd56bdd4ebb18f14e96e4eff9501e15364d9a0cb7635d92ad4939ad41ccc3a34ae226a39259df5f742139ebc52f8bbfe903023c6bd7a6c29fff280376ded9dfaf44d47e65a17384a7bca9022a5452abbf10778a5f762b032b280f6aebf4e733d5df9ed223d45f70c79df0d11bb1c6e4a4f9bf33858b600122faecac8590de847a1da2ec60d057800592d0a85de528a30173ba3cde45945216599ee498139dc38e7777a75c981d6bf665205eb5595d0ff8dcd999658c1c38bef7f5be028ad085382878f9b7ae086616111c16c99476848a68c0b088f72af407ae773bf9f8f16ed78eb631eaee87e60617aebaf88ff75ce82db1429b2f4f6836e34d01a1df5ab7b8b652692da1a59113710674e82f3b8b1f1af734bf6a84b82f48520ddceac576bfebb346476fbd69f9831bc8f6fe8cbd820fce048888f5efb4f4ab399ca442022c722bccec2e0b34595f8c554347581414481397e0e565f7daf4ee68741f433d6ce30bc898a601c63c8ae63c319e457278f6e6c15ecaf3695798da5da65ea95f38e2a5d6be9d8276d83681ffbb7a288828aa4c7b313ffb5f12b385cb8fbae2ddbfab2de7c2ce3de015860268cbc1fe3e4da6e7d37ab17cf7f94044c56b70b89ff789ff8049ca883f2aedb9621f24ce74990aaf32b0bc4b46264c06042805f7f86f86d06346626522422db47d268fa840c22e412810dfb8831e3180342711d5671138c800b76848ca8db1cb4b00f3a6932c058b63690a1a9bd2f4a753536a1b19de2827e47e589a827bf2033fb886755f1a0422a5977ed686cb7589c83f39448603fff6dee9f8a5d077161732f2660a5da9dd3a4d71ed551cdecc8838251e455abf493c68b89270c66028860080c0436c6f345e7bdf33dca094c244ee885ac2d6d5c31de5aa4e1f947600be7b35d4a2f137afd088fa84411aef2df49d4f9bdd94f7f37757eb6612209afe62c802678f0dc52b122a30bc744d8df46918e535559af75bf63f47e2c8a7668c8ec66b00fcae92114e3357304ab18a13151579da7fb6b55dddde8faf9fb4799516a99e10c7578a5c30b6e88f8af80271c619b4bd994ae2918bac92fe8e373201d620b8159801aedf83a81eb858db35506ec9cd8f2b586ec7bec255ac752bf5f2a7e1cb959f7163bb7f95dde2a944f0ad3bdcf881c18b5c790e1086f05d678f0e18582e1dd3d0d6d7f886e13b77c01a30fa234453dd6ed6f388899d9dda905fc78122343b6d3c35c63a0af111fd97c2d43ddf0a5d161b719a9b4a6115f5f526e99e16e8c3e8de68212a45c01a5bfc236cc808b2dce2b03cff99e55874cfb7fe308ee18c5354d74add21ebea2e0d16726560ce8cdaf1558aef5aaf7ced629d4733cec21459e18a91ee6fdf24d6913d08d888f2c559661d59cc365922bacb20c424d34d64d9e6a3e45eb5718788bb40cab4ee91a2c691436db5b1b97228f12333b42b1b117861d663b5a3fbd33b970502938b8d05a462332aff6cd16e5a00607d66e0085b639bb42efec6a80f199f69bf70862a50a886c744292d44f0a10c3a680b8cd8b7c4eeec11b1adfada0ea61d0ae1455dbd991278ed6158f649b0e08ddc7c5953b2df620d6c8bff58673448986ca4b0447df4f4b8fd523d000414ec5f3a74d9438773dcc5508695859c5ba8a118defe46142d8529ee471cdf65b64c31e73b232ebcd48f024ff73c5581266ccd47c490ac8fb59ea36089186d6b8889309e7ca31dbf8c9828bcb9bb93dd768737378fb55af07ada1b7892cc2d03ce6e4a9ee35ab91bb5aa5499863cf59ea031e41a0eee168b471f25d2fa5e5c0dba0fbaac0173864c3794630ccefc5731671e6c022e66a40c206ed5b55b31aca0b5963fd3348b586798e5bd5ca944b4b76fb6c54be8dd405777c6d94a6d2e35d195c02315420e7b0354c1256a6cfed21a2e7261838f7b4646217b5cd8c861a5dcce76e4493eb144647ded2ddf821f0e0fd040ee13e3ee7d2678b5d43ef7b7c452988883ae8f7fed70bcf00999d0b12140cda6ef61077ffefdb7bb012c6952cb491f10c7ae6fbfdb473f11d3db2c8cf970f52335f8dcd84472deb50e4defe2fde6e7ff96ce6b4f71c8ec783063a52627ee36489e69f6593361b01e0390f942bb0e7add0b26552e3355074b5b7160518ab66f21c19e22c54577da7b92f6609ba16a16bfd24cd1a4b5da436d9ea13d59681fe39c8d44c8b7a3656db70c87c3c2be877a605e7df5ec61bed3640f7ddcb976c5001ef0f224d1f58321bd264544cb140ea40b04587cbaaa7c2259fc4766bb0a66c8a9745300980c6e4914b9c9c9592b8b11451cf532c166c0176c766c99a8c53caab98f77a7842317c9106d2d111a00c7580bcc04c996e3030c00955a25fc1323edfab91d84bb1210553c9c232e9216c8579188d6923b67322c2a4351008380d295a2fdc685795346725d8d765b49f1a2b8abd53e015e7009e3d76a15e1486d7000df65dc9272b037df73280c5db27e276ca9b289c83359d9f9f91662bd84143c0abb620f2afb719131196e526abdb156716cd6e4094226e2719b477a53fe1169f4cb5521c52720d577aa8e7dcd2d7cf99d7f07d5c16cb9e157caf6357e09b719beaea0e7ad525a9ed0e040e0471bdbddc06f603417e9a7c9b3ba89d29152e439b16801e7f52d7e4700c08866ab9b109014955cff16e8d68ecc8cef0a53281cabec7d283129ef20e4c747ebacc7cb7a77a329e292ca3b848585848fef40fdfcfc68972f39021d4dc01d5475a374f0335be19ef7214e1059695f4e1b0f2f3926074dac489e5c4e7c7427ce845dde587a2dcecf6602bb4ba72df2aa5b50f1e3fa01b685e946327e8b98b04184d8deab0681208c7d85bf708ced936505a5fd4de5b944333e1ebba600a6649f44265e43187f8808921bcd3202c334389cf1603fac1f87bde986c0bb7e37c00bca201eb91cba46e5fda23fe0a209b7ae61ae104f8b3ab1604cc4fade60bbd3a0e83f5037b45eeb6155dcf560b0b96f7187740c06f89c1ada0cc70970e8917653f8bee595bfe06382ab227c4f58e71ca4618459c9a373b65595424d3a816d34f73b5bba7d7c2d336d276e111763e3fa526b1abfbb2faec4984f1d3b0cb78e41af1b818da8454170c3340ec336abf52fa19559c9f3ba9a43c2d825b1036f39316a4802eeef27d54a6baeeebe4726a57052e8e943558656a9cbb1e7d16409cdbec4102d31ec9e2a6b6387efac601e34ecac989913809c2a634d1837af2f092e04c8d158b3606b54752edd9e3b41626ea541d17bfa1eeaa59454f24b3e95594ddf65691081028539207764a40143df5c3abe573ee2865b7a41795113003ec31d531ded76fd67e826f9e35d3fa3b29af1e276a68f2f0dbe5c02ee48b7b8efb603e0a7f626060af541d7f501679c5d018c9d1b4b23da885d8e47816e98f83b3fd306f709f3db5d7b96077f5ac21ecbc4d41ee55a87a19e7db1d0d15fd4ee4adce9963c3e70ff910e4a7ae0e8d6c88bc2d391ea836cfc00aa70beb36525deef65f3bedba7b996b74f1da1c156f119322eebb4043006efec1b1a918aaf27089e61179cee574574c311bfa4f5c54c26ce187ada0988cb18aa6f1bf91c94f02b8e65ff455742b950885c7e2f53ba110f051926f85e04a978b205506528a1d21ab444045aef03c8592092b292742ca671ba88b7d66d18cfcefbd39eb16ae95d9595be995e4b7fff77c7fdfddfc1131a3643eea235442994dc54410520c4164d18fc7be17210f1eef3f74f3fad8b7493d6e85891ad6d23ad114208d964efbde5de01160d6a0c230cd8df8a4a29fa654abf2c32fd2ae2e458fa48abf0b673b7c77e1f667f8743eddaa78aef92a8e2bbff8061d4be9369da3b1ce2308d4c167d2843af28a8c9f7c8ac041581c88cfb7234d54e9c2cd54e349a737eb7e93cd45d7db061f7249637897df697335966d84c9c8955b231f7f923dd35ec837dacecf43b9d897d15835cec71227de1b55659955d7b04b10cab6ad87efa11d335eca3ddf413f6b1b2a3dd845519761fd337acca708fcc118c2076f9c53bf615761bec39d8753013d6705cf56a0d6f621f2bf63ef3dc1bfbf4bbcbb7565865f14ebfc32a3b3f95c5aa1ab8cfef74ef739faa1ae6b9eff4bbee4b4d1676ee8b9355bb6040bdfa6c260b7bf6ad7a855dfb30217a15b4ca1850c61e6b30cc2385ec6d264e8331469c2befc9ae1ec18bb973ccb7752f56d190629d6b93a3a2f73bd9bd86c3636fb2ec43eb4520f4a80884be5974f6a0851c5f466064981c5f51fcc028c70fb24c31821717ae289430d262f49b7748e5f9716a78d3ebd348a36c431188a0051a4d0c4901827544185ce21002106578f922e7200a920ac628c2082a201125c890a28b163368020ab085c0230ca3276a8006192678033422c41157bc0c6189288040e9451ec11a694041831ff8e08b27a0068010841b6b9880042d5842fe3c6184114926e0620955dc00bc000d6c30841a50cad8e28d1bd945eca20233c608dad2841857c0b6c096232cc1842c5e00d404d85c64229660043184b0441366800d84082ee092a4c6101426e416b90d2db8604328ca9313c001760d295842162788a0850a1b60cb2db98de8115bec47d52a0291cf968440024d649cc008440114475b93600c2530c4bae0200964125f3298c11721360490494c0dc089effc2216335c5005dc2a37bfa8450b78a00402a18c2790c00de3e6274df0839011d89fcd1570bb588b2d8a2802fba381029a9184173f67341e62f480470f9890d27ee0427e55c4162b166cb1a61500a32cbc1af419a3496bf801679226ab2593349354c30f38a33459712649ce57234040a767ef543b13d47e353cd35ddcba5e13af158089a33af22c054b60865546543bafc1c208cc700c40958913c1d4fdc16bcd24190d4d1c1f411327366ea5e467b44c1ceff233471307e6f233481367bbfc8c6be24c507e26684668e24470c695e56592647ec0d695499a600d1646a096ddfbba9b2688f226d801653fa98c7057d910414e754482db25b861b1a228cf28cd24f54dc462c550969fe132b33404f54d763ba69f64d7fdd3673a793fed989ee150bbc974edb39265dfb42fb4d2b762a4c3a10473e4b8f659e9a3700873ed3bdd73e010f5ee1bcc37a334b33459f2a66fc6cb64c97b5fca3559d204f3a1be79fa22987d11ec660c3242b931f78556b6670fb99fbe659f6a9e3ed5c4d93becd33dfb09fb8456763a7a53ec62a0a2618a7d0481928aa6c910c9204d1caf6794264bbe9b49ea95bc4c912a004301180a4095001005a0289b499a499a519a59929ff122cf833bc29311caad785497e0b56692fa05de73eb4959fe0ef8a284862be80738a2442fa44411832b4e30c69012281f25144fa703b0fddc3e0de11283479313cc302135042c3789060737c89de5182959d4e505e96a2f5e4f1091a91255bccc2a22a71c7fca5442d05f423315d7bc676462d51114be807d090671609f9874cb2327ce78bd9898f262e28aebc584d052521b1efdaca90640b269dd1c3f310f84bce4beec0f60999a717f765cb87c356f939b5c82e8d7887974885708e5897738917885508e38ca16e49c5f640b546cb588993471640751907e7e3b1e483fe7a4424c9618b1892d4d78c9ad9b9bc8d2c411b8d0249ef822c627a808c2174b1c392183dcbad909a01c7f737c3931841351724c129ad2d3cb9fcc75d4699c383b95fd071474c1fb8c498eaf5ff63dc90131a0273080d88328783f3b95c546b0c7bce381b78b5cafc42657dc73ca54ee5b3f797ebaf2c53e8b65b25c148b570eaafcb584117d2d51d4b2718ffc22230d36c9061d224fb94a69a414cb2c53ac22d2a2997299621db89cd281cb353fd99b2b906d784f70b56fea9053a633ab5443b03c2f73671591ce1377042267ce376f3d6977b794b2bb63777b268ac4bded5d9a948b7e32de1b090717573f705822a86609d711652871854aeea6bc37654c72b95c4168e479ea9520cfc7dc608c3c4f335f444ce489145f4a8c8086e6eea68d1278fb0aef95518aa1200e28344bdfd07c299dd2098f07b91fd32f1b0c91db8a097237e7e140fe10242f9d739584112847b5ccb6f6bb3639945a7bf2ea6fc4e91aeb65c5b77f2893ee36436701bd92538a00851b43502103252011840435a4b862892625c832840bea10439a4e4a051643e2624b504cca3d628b31015b34001088000417003d31a58aba82388c9090c0650b14f60a4f9e46d4eb89245e4f440124b164f29a74f9a08473e5f936ea2e94f652df348e4d6a2d2ddff22ddff22ddff29dc61b4da37cc5af2c1fb344527f8f587c272bceaf1595b28cde125b9459c61738578e3f70e89b56aab3a59c7918e617302f43e344a37923f33b48fa26be908822cf6380762a668a3cbfa2f53d12ab88d0ffe439ffc30518e617307f7b1ab96aa6508c31c6186394b88b40ba64238a86783dc125c7a228454007da3aaff050952e2dfb467533f7556d818704871cea93a8acfc8c70c0407d0e8d0300b03e49fd92c68947f5613de57a099444aeb5b65ce52a45aedf56130787c9aa48d54b45a4d75a8f903411b57e556f539f53af53c1f024f6ec18f6b9c79e611fed59760d5fd50ef6fe307c27ab1babc1a6e44b6ec696b2cd423148d53285a69419a5936e548f706dc28befa489736f27a53a69f5d529befa922df437aa63a80644134b83e6287d73ca3700c8fd39949471a851dfc4570d9072bf66e91b493b243a93851dc9992cacb7d92169249b7bb13bd24793d55fa976b4d491d56461473e59d8ef2b568d1a2787966a45dff4eb51e30000ec57a42bb95fc16836c07ee5d237f1550589dcaf4a358da5eaa5a79d8f37bbf603ca0cdf552b65d32b2cd52bec56edb322ed0fb829152125cddfc9aa92d392415d8da82b4868e2fc809256a9456d54916a5255c2b460476d84256148554b35f266359ad5c8a82e19f5aa8572cfa19aa557461148bf5ef1e467ab1ae53eb68442020039beaae080ca080eb023a68d01a8a0f56d6200b0b2d9bc4489a93c1aacbb6f0d95a0dc8a4631507982a5ba3d2a401e15277d9bec8838726b75c406d6d8105ae44681ddd8a5930b94d44173313455e4568d91edc151aa870655a420430b31ac2803eca035a65c31c55015284e80bd832c5815d8b5980ed868274c482401832684a004294643007bca192888f28415407091040eb04e620649180d6db1a665b35dfb867f84d0509665d97ff2bdd9bd73894d75249467102d91f90615a604869812b83dbbccb1a66573535fb15e4610e5558eaf2943f975842bc7971146b9e5bd8c38caa878034e912267e20b316949be004e2142b610734b47116cd20dab6288f9fe27c7af7573c4b0d71429761e9b723a41a3ec4939131376abd24411af26544029ad59e6e5d54490b516d3b42b60bcaea471efcdb62d4bdd22b9d79525bad7152a39c6c4c9ef75650527aeeb3ad3e974c565737c5d11f25e4cac81e2727c3161060c8742a160b80803c3c41626b8508d8dcd9b928b31c63805ed9b08c56321c72c6244c9b15e0cc8bb9c6c42ca7e41125cb688d94a678b52540c924571bda28f315956f07ac1952a625240848a2280c46041166994e08948842648b8d6d0d2c69333ba0a0c8cb4c0872b8c10842014a0e007104a526e00051a558e9c78a38260285a1ba80165896bf5d2dbd033b24c575aa5f7d2148da1a92e99faaadea63ea7d62fa071aecbe5720151299d73ce399bd65a677d6ad65c6badb5d61b11a325954da4851b34f801ca0b869630d4c40f146f601145880a26aa28a2911882618b121738b072041c4c818512cc38021329b8e275c69530b010782deee68e41b5ddd5f37254a1821ea4808dd7118c702399c8f4296a413a440a315420e50625d0a208a438e6c876f7ff821ae47e4dce737cbd60042f685284cd8d6e94d18411ac208c3674b0455c830b127ca00110406148b1c416b78606c16bd5502a6d88a04fd164fad58c96ae4611411368e00408405cf800c4029c0c95218490124b582881a5768a52155c2a43bc9c479fa2ff0f0b9456a0440f809074032c407a9b898373c611161d10c9a0cb0e404a140596298d1307031db9442e17033950239ef7644ec95ddab469379d3d1b06793bc9ccf5ca4a69b15bb3df2a8f555ba5905a8f559da514fbc9c5d8b7ace52c8d89f1628e99b1668aaf4bc62857ca5d34bc16955cfdb8cae9b48c119531ca4f6cb1669352dee730711a44c00a5625b87c928ac363c384841caf01896392acbe8faf8941b02954c8f19ec49143de9b0b99fa90944352e6ec5812c429b2fd6b07b93ebe643787a4919090a6c8548a5ce314b97ea398a5f4bb7165af7d5c5cd96f3a7de3c5554c161d3c3ae627a754c1724867e2783bd89f2df656e756dc622df065facdce963556d3959adca04859a3bc94974970dae34d7d4480f618e5a5c8fbeb79292d292da91f4c86c691571c19d9914782833c061ac7d3413ec9122959ba302bbb8865142965b284a44b3ef58a578f612ffea06fd1a25e9aae5e9a38d9ed2750e3d8379a656f336c95ee8f1f35354d9369ac9d47d9de7a11b24a6da4d2a2d1512375522b655fb8ddc787ec1bdec2ec5b0d11cc700f6117c09e16b5500bb5941eea2a4dd44549494a4b5edad541444445465a8e90b8265ecb2a65fb162dcaf6d22583a49094228764951844c6b8b2925d9a383fa07df6fd80d68b3de6c282260e05ed31a1184466dacf6c148a9032eb2ccbb22ccbb26ddbb62894d8826d7d7ab7b07d7abdd457aadcaf4598baa0255802d2143279d19cdc6cb26ff3fbe64d9f5e2ff5154d0eb6c9db2a6f9f77d3302797202fc6b0c5b1628a5babdcdd3994db648e9994a3e8157d16a35ed1268ab1d6cfeea25ed1bed22b3a3f9b4420f4f39b3ec8166813af35898866d1349a5ae6d144eaa33e6aa424a5252fb509af3fa72b02a19f6f4dd7d292977675500bb514232d4748494a52488a1c9255888a7a08871c9280412d9b4c6d7274643082846be251fad6bcde2a0619aad244b38bda68b6961844cea4ab844d09768f611fecf71af68b55f1d68b738c60ef86c4203312ec27b30c13a7414b29ae4a783463d866b262d26cc166bff170f39cdce35bf2058973640bf6f65a0cd2384804621f9f5d9c63c4ddbb799e964a65b7d8e224992c9b86572b9137313581574f53bdaa41e6cc37bc56ce0b5a833e5edfd5827ec0165ee496cd023c1b840c0417bccb831e52e8e0654e48c0b34409f06e1519bc3bb4da820504388102b618c1095b00f1f0ee1b3978d81a1cf0608890e34504b240e2693ef8a4608077737c6d7902cbf1b585684b952d3ed82f52322173c24b19399a702199aeb8ace09c08d2c1e94ad1121b13432e9869c2e8158412744b04c13871a445139658ba9ed4a20c1dd2051b9b9c25b4342b5ea90592c963e2a859f16a71f4d2a24a1c589c5957200a2b05f05439bea240812a8a2630789e122ee09dc8d080c7e5f882820b1bbc1c39bea0c0027aa61c5f503401c48bc9f105450c6cb418ea2cc0d04109b2404a6243da929774ceb1e9983326070caadb9bf334e79c73ce394db935e79c1db775b796cabafb6273ce39bb71c4b66b77d3796f2a556bcda194b6f46ed411930306e59d4c1db769998c294620f13f84b035b5a89bc94a0f827a985ca3fc66965ab7d159cf71f25342f3929a6fafe4a722825b9e0b2b55fc4f8e8fd1b2ade34c374e968a887c7f4e9de9420442d3f3fbf2e69d3a21aa483f19b1918a63de894620c5291db966177aca38abe9cbdd4cfa677696c6764d89af2290ac6fada9d4bdf10d406146bbcd39a5774a4d56bcfd72229048692bc6ce14162bed960bd96b25e81a9e7cd4887a528bdd8cfbc43975d2fb641015e9da382ecf2473db30aeab3451bfb62a35db6e842131128fb2adab18699d8466b4655bed2a465a173591d64618c3b239aee3bc1844cb9da5b364a145f0de5d13e7c52059d6d2422dd447f49418e3acd9a6b5d014aca360536a0b4dc186a460536addb4ac85a6604352b02926ced409d96ee24e5ccb855c02eef1395e74c4175a888879b9f20d19198c99bcb410b783da755b8893c75ded743a79494a6c60495d7472727292ba70e9c225755952ead269a62ca9cb92529793a79db8532784fe6442710b681ca965b2fa56e4782d5b60d0391d0c9bf802c41724182ec420264934591b2abe20b378ab9fd6ef621f074617634507860e9e9ce9c8d937d9651533512d128c4e22c9a34e82618153f6cd33e1d861ad13927ddb348e28cb15198ddb88b2181565c99d61140a05c3b5910d72bfc5423e7915474af90f1b1758f10599c4e3bdcf6b1bdcaf669d94d237a8b8d241844c2b774474701d912fd7d6d256749d45cac441455612df0e392493c84de493190ce7c520a6ec62da668a7de46304477859f86056ecbdaa69d664b57ee4f945ab1aab6eb8f41af6b9a7f7de53ac6a55cff9791ce673984f32af95d795259948e8e79cf394da7401e6ef97335973568a53b30caf25916c3a044ab18002233d9963822c0f838e0c46fa86e686ce1c74381242965b96524a296d64163ac85293974592465e224930683c2a42ce4eb33fbb58400cc38e58908af400b9628194eb1360ad150b177c00f4c88a23b8b0e20d24206ce1e20a24f29cc11540c83054c1459eb30bd8dd3229df202e0aae644a4f6f77a54859450b54991ebb4273520144032aace43432bd4d9580522d320840a62f2d4799fe00fc410b324523d3d74e087d55c1ab08999ebe407ace8ba179cdea47dffcc814cb19993ec9f43930e82c60062359dac8f43b3c00e92980854aa6f43a2891e997f44d97e90bcb0e327dd8373f5c3c8802a86f7a042d659a458d4c2fa334bd24e2c5032f32ddf2ea81954ce3174441395291e9cb075a647ad44313410873a0f79a41ee4faf5320a54920c4ddeb01600a3872f7f5b61adcfbf4b1cfcfc7ca4e1fc3a9c9a2f7b98f93d543929ab71be6b11fc9b06f98116dda0cabac668304b3ebb436b30ddbb06cb6765745f05a291b220804a43dbb3dd5322f2dc5b3f9119528d8d376b141e007a48f35cff20b62ca9225802c5b06f05aff4a3e4747062313673bf7ad264905cae7c8c320bf007919669047323b0a49295e46c0352b72114813bc7823da55aa23b30b98c5898aaab9e3254e2366ac7ed8e4c0d0385f401aa82c3f0318b3630f378c2c7b2699f6f51e03fa713d2082f43addc3b79a2c79eccbf964d03e2b5dbd922b29b39fd997898d52492556dd80bd56ec15c32a23f6530d50b533d500b157ac928fe0c52a499fc41ebbec6c90e0bdcb4a8cdeebf4e6ca2106893528ababaad305a0c73e19e6876455f16ab2e8a792d78a4a3ae49024764f4d9cbe6c9a842dc6df782f3e151ff3166b368de6abc9aa666c41b0acbd3eceed9bbf3fd2fd9bdd54aed8f6ee4736ee1d877d38ac56f92555fffa55bd8d86579355536b78f7f34eb2f755462656619f8d5518a64064d909511999efaf83eee777ee67387f8fe19dec19bd0abb9f0a5bf2c9aac7be96e2853da46217571542dca80e497a5092ae1c62902dcf63ef0153c26ee2aefdc876d3760dabe88655b6fbf347b6b9dd673e7a39704926cee947ba73dd4f1d7e771356d9d42ad5610ef79024077a9ff9de2e77b8e31d0d639bc9c2ae7da3365992a82acec10a2dbdc29e85a3f2850eca71be7fc3c4aafbf9ed8d55d90d1a565dacbadf99dfbed3d7ded7ae23c3646118f61c92f440e50b588eb1535d5cc3fcf61be637ec37f4358c64b2b0ebf4d13dda624dab85224eabcbd10899d688333b8b75268bdef60082202c9a68492199383ab375aaab7a6c95e9500442631059f4f494e9cd928e66cca7b2885efb7a982c1a945d6c04fa6b54c4a3bae1c9a7ee27ce9cdd7d9fecf311a436c9763f95922232775003750165919daf49229545f4b78725b2e87ab32f0eb658336fbf1dfb114d3b867dacecd86bf7c1be3d82d108865d75647601ad9c487a452f64a75742a6ec19223312690448d228ed2ff6a9e65515ab6ac09efd88fdc53ed8de273b483f6fb2e8ed67f35dfb45b0a5783ae01c7a45b9c425ea02bb7913877ad49e7ef3cb99ac55b638586bbf8a417a3ee565c95abb5ac3435de6dd1d7527309741615595c1aa5a1f4e9dc6be982b4c6d4a851727eb399365adf5b152ef03f3ee3e32476119e6dd7760b00a3bcce9bb57bc2383551856d50083613e0f83e344fda23e1586194163904c0b6164bee19dee216ae6da273396b5af8dc0a03421303038b4a23da4efaee1701ee63a8dc97c36aa8b57587b40683015f51f70877b77d477baa37068257be8c11c75140e4f87f90fd840645b966d2a0d1be96e43043bec6325bb0f7d07837de651aa1ae861be430f8355da611ec19d7995865535cc1ce647660e837dacecc05ce63e393ef323328f790cf681b9cc23783f0d88bca98ce4c871d591d9058439cc35254564eea157f64296f44a9bb9cc751ae653ddc73c82393ed5c5aa1a725cc78fc03c06fbc41ce63ab08f959d98c3dc47c7733c82323130333a7280325f04757c11cccff0ce3cea3a6d6d26cbfef4e94c9611d367bfeaacb5aa1ae8bbefd07758751fc13877e65517db473035592b7b1bfb1c7b1dfbd81901a296fbb17d8a62e3c1dd06072123b934d4472ea2992464449786ea918bc8260919614b43f7c84594250919694b43db918b884b1232ea96864c472ea253929091b734843a7211c1240919e5581a8a397211e9489211f2f242baf242ca2273f7cd8134b22207298cb20483522acfd1251d58a1832c5210e5bca4a892e55de85446b6db10c16d6b19c9f3f4dbbe4f96dd81961ccfc8f1a50330a6098c649b184cb309de71c8617296f2a84ebcf8786eb3b8e57531f495bcfa984a5dde26474706ce7ed7c166dbb0db1b865676380e87dce3643159caeced75664f489463042bd83d33eb60931bdfb86475b02969179188f34519629054ceb4d31b3ff0a2cc9d29bee33bbe7f8a8fb16d6051df78e24f119ba87c77938a7d648aa50a368aadd4925bca6e866e4eca198af4ef8d50944d9798677b77ed6bf54fb8a5fdf4ed743a994ea779e64d72261a3c573ed1205df9f4f3703f71b862bf91a24c98ebf3dc6b6fddc7ae95ddd4cdf09666327531d4fb38bb22da4d11c8fc290299ae4c6d168fba9ae4d337d34fdebd7353c9129b39259c777a67d3bdaf07857c3af7f524e074c231fe3051aebf9676ee527643221843703e5d7644640c9ae413eeee6b71dfb8ef3cf2315f0c7bbdb218e78a6364f64c120b227677636facb1969f1289b908047b0c31371a160ddac52042b818047bacc1c6d8fa14b7b81de21522c094c21d2297dc3e49b4d6e5d141cb1ade41cbfd9fbcd5eed7dafdfbfe5ab640bf51d9fd222d9ddcdfb2fa2ac395639c2237a6b49ec63e62abaf9708dda86d9d292d0f32db8a2de6b19f4b32fdc6256f9fb9f37619af10ca1ba69437dcb2857a9bab51b65075e0a4e5b138126c9bacd94b5b85279fea55c49e7df7b2ebb917477a31d72b29c646a412a9aff562d7e97b7beb9d3d31cf3cbddffc3eb1cb6ff6c52decf2f393b36dc7f0daeb0f3b86f12082439e3fee75fabe859d67ce244d030ef9de39afd3f513dfebcc63f6d423e98f95f58ccd00db7622626fbbd85de39e5d8bf952bdda789099bbfd626c6a92f81148e4308fbdf4ae7142a24579bdd26e93b9cb4e88fc5a32f3d854cc1681c41c9f7d5eaf6ec624a6bd8a3f71afa7397642ac166fbe53567e97da330db768d666b738dcd2de3d5c9e72be67e6793b8637dfb37b680bc32c642019fb687a15e56d109b14421f69a0dfb6dbbdd23e9fd2b60db7e4273fb9cb2ef62ae3be25b3a39ca6d58fd9ab16f629257613b0a40ae97ba345ad6afe5ed978c975af59d1fcb03df0624465691464646478a8687aec9091c19e78f23dd4e58a57b42b26d5220b06c625ca17a492942f50d70b51b610e190ac298f5b4c72af915b3f9952dc6acc437b1e8a83b93978a0272c4374c93cc464f979aec559383c79ac13d2238958578e606429a5bc534ada52da9549feb68cad3ed6d47a1fb95533104f631997b7d8c664aa43116a9017620aaf25852842a60fe3bdc9a23f28e5837280f1404f1ac41e8480200480a1951df9503eb4b283dde25007875676ec250e8fa94dbe107de3a39b16b14599222c659a1c5f45d0226fa758637c6b56dc33b1acb5c52463b6628be7a572bfed10af106a6bf10e914bdde100404699622a3e4c708c3de4796f68a18b17954bc5d5830446441972b50f93c805cc1edf32a93074032d2e9a2472c9545c2d74f172d90300195d57bc4248f321720133ec8a5cc0782a2e992128f183192e1718b10f93b792eca0c618c2117100b95c606c408edab606647821434a8e305d62a4f8f6b00d919bbde2cd951783ad67ec18f09329fd9152c83644e6fc23650a50c8aa2a52e9abacb4ca8a7948506dadd65a6babadf2f5e391983b820717985c669ce127f344dcc3244fa3795f6f3f9e7bfbf9a98a4c9efbf998e7e5b7b5bd4edb2ab12fa9d8a57dd74b21d2a875df98878acd5fcc5a4ae71a5bec1b2ae7a72a2279eafb53d2d323db2fa9b91f63af785cdff460f962a76f5559310f6d148cd125c7d7185572eb94e959c8f13546517decd5fcedf9c9cce35155f1e5e18ef07a98e496344b9494ebcf4f9e794bc516ad91b79dc800ca3d4f56891e26991e035bec2de64837ae6f5445e6972f799e6749e70df0a0e579d924f7c853fab0c5a6dfa8941ad89afbb6a4a5d5ca66bec240939494e7635a97a78f63a362af62af38daa7b84ad937548c2d1df7c52686962a99364ecce2dade37d6c4ddc4d9739cc9647ad7d16b2653f78c93f49ca53c14f320bb22f4316fb31b626ffaf6a988b46c0cdc6304224d9f4ad3de9d7634d05fa7c528da708be66d360f379b6ec2d474eee3e1666e7b8c4138dce2ae43903c3713ee99d95e88fdb4318bcb87bc9d620cf24b6b7bfd9169f2d61581d4635fab2f3359e3d83e719c9ceebddf09762732b74efa4e76dce464de0914f994931d3884f9e47127287c73e2e1d09b7732f31d77228343999f4edf81fae9bbcc43191ca26abee3a1ccb99fee64e2f0de6313871743910fef4f38ea9d93ee6f3b70888fa5701f6fec3b1ef677e0b0d67b38d4ee3ddcf1ed4ee879e03026e630389cffb687df371ccef970bbea393cef1c0e6bbd86c389c31ed7eecd7b0fb97b38ac391ed6b3aec243ef2ae03026e63a70a8e318f613701873c275603aaec243ec2ae0f0846c9ef030abc15056397098e33a745c9543870efc1434ed27f4d0b44dcb3e1d3b6450d3c33ad364e6dd74d4d357b9be3b50a7be1fe64d4fc3746c9593d92173bf13aa6a5936370fd381e23c998cdb81c2fab676faaa8e5975ec98d7c70aded6a76f3b0b2815341f3e72ecd866c6623decb358998c0cfecabb5f712bacf050fb0a2b7c18763a3e8bd3ceaaabd5c3ef2b1fab7bd2e11d7f851da8af70161eca9c0516ebe176960aacb3f030ab3e7c6cdff15755c757edc3878f151e625f01873e7ca0e6595041851e0fb9f7c88ecf3ae13d529061e1ac873bce5aad00701570b87a0a2a9cf5507b0a0f3d5cf31c397ac7f17df4e8f1144e38e1aaf748e13d1ea2dee3dec77f3c3cfd87b4a1390b0f4f6741d63c0516fee3a1cc537878cf5ad1d0e0d3b04ef3f03b8d7cb89de62aa8544f21ecf114ae3ae1aa87dc55f261bd0a5f3df4be9200f0f110bb0f1c02c0c7310e01f0134ef8eae13c7e98fd070e5758e1353854e150f51c394e83431e3caec221cd73e4b8eaa18eab70c843751e0f39ac1359f2356721050ee63d70c803873cdee31c77fc503bc6214c0ac70f379c24b2e461cec2430df71059f22ce070050c45b2b6ab80434d7b0c0e63bea9f0ed6186916c38d4b6d3e0503b979d7bf8612391254ff350079621b2e45738f4a1e300c021c621be0adf6b7098c28f7118cf006282155f5ea0060a82c02f0b20a260e1461286709205e8e4bbce0e7c3f1c1e5f06a3804fe8b1e3343854a5ce03873f503081c611d211485c0105b8e3c7053e08e1042f2f9880040370c743144eedc0610aaf4e7806cb3c9c3887c89297c1a10e0c45824e508fc1e1e93930141bd0c9fc31820a1c548cf0d2e5084a0067fc628989199cc0055e5e4c009dcc9f1e7a3827b2e44f3834e11c0cdbc454631c224b4259814eec351cc2f0441448b84197202cd500cc3094834e5098828d26a410c21337d0009df4b18717af62687158f1b177262c0904095bb42cf0b42d5a233ccdca604ab430f062a7c3ca600851e4d6cd318611e41586143b64671046508eaf2fae642e727c7d91453ebebcb0f52d7b3f3b034f66d918b69bfc36656c1377b395b6b3ef6e899ce127dbcbae4708112fdb6b97cc8cbdd56ab56c0e495633fb7ac2164d9e9686faf5cc4c55f0c59435bcf8fa224a8ef32f2144c9fd9814841181444eca60f3e2a828c76721c01669c81b534a6c4be8c249115e284109423060e1045558c1020711e02012830a18542aa674544441c5cba690486083801c6e08810a2739b06e2aa4603f80e3cea02343174878c10f8ee6107e1084ee07493da0205d0992224492165bacf4200c25d208e3cb129ad0e9020c36395d18e1d5450fba882288edf64d8c2b98b6767bdddd8f40e4cd280f0f335301413d8fac234785a944ab18332d25391a31ac2d0f9d95d02f99d8656c818695c8c4b01ae5a4f406ed1733d1b40751190500ca6e87821a7ef2c309bc6ff0624abb4822e39a782d1a1434715446327b2a44958837b4495c49a143b40a25a24057b2bd2adceedd8608a6420fdbff80de51b72182281c7ab8e5796fd9543082a8dbcb1b992cabc48608ca1066f888a3e2ed6ccfbe612b3bf4a58a622340dbad18d1702841d34fff01b7ec5b967d7bd65d82ddb3d3a218c42ed599c6ec329766d2549a5c7a6531204c082c0873d9377a652fc54104623f3f9a03d9423d4644dfb46851a6c7a4f44d370e0a7d13b3bded626fbd348eca1e03b2c784e89bf88ac113d9da6351a2c8f6d890ad9416d953a34c4ab6f65826a56fe2ab8b20b27d4644dfa090ed378ceb84b8543bda8f488ed33a21744bafecc140ea95a55a62a455d277a547bdb2d4eb23f94263ab245bd0220691d89ed119c9ae7d3fa0c4996bb2ec65f622895c9f7d59102daa55888aaa51d5625f8f6c9295f677c9fe7ac98482b2cd5cd91e1bc3b34ada617e64db0e837d72982c7b9fed1a0ebdb211b456c97ac9b05deaac12eadefb28ac921e5649895519ba967d422af90d932235cc3559f61ab64b9365b1a06cad976cad1276c2f898c371686a9943b3ca249a4546f4d2d9d1a208c43ed7699dbe6f68349f39cd6768686868349af7e84143739d3e0d0dcda5c1aa22287c3b0adbb6994c720a8f63277cfb09b8b3d0bca57a7feb86ccab64a6f99d69f4a81d0dac99f7f8cc7bb4ba87cdb199aa90a9478fad478f1e9f29ebf1d9d1c0ca3db0aa48eaa6a76efa46f329a199e6261ab3d92b9bc96432758f5d0364b6d784554576bcfb8e77dfba6f3bcd77b31cda5a470f0c8976db967b7c3d52667b9a2f664df5f1b8cee4816de9157deab3445588569855a122abbc615511997797c1d74bafe83b2c8774ccdcc29032fdf58221c57c960caf73cb6ee91b69afe4f82c199ecc96a86fba561552ea1b142afbb2674a7d93dda0d3fd61fc35f7b5962c4bdf4879c26f9d6ec09ffc4edbc45fd0c10f4f0e7f714a3e7d76424e5fcc27dc597a45effd2e2f961f8eb68b20bc642039620942cb0896324e8e584650244451bec9118b10ae208a822022072104943432162860649a1cb140d941de60380fb7c2ec5d7edf177410634e7edc391c7e67a281b10be079df36eea3979edc38ee5e19eaeda7928ff927ab8a78b78fc19b6958895ba19c4b9a0656965855447b77ed1dae4210b1654dda4bee12b7de6a2db275dfd83a6cbafc5a97fb9410e1ee9d08cc394f0911efa81339dd3b875b3098c328dcf270ebd4c9af0701da7b6447032b6b5855642a6944f4cd3d3d8f37e4100feb86d723dd70c30d4f62ec315332bcd64c838725634aa6f460d8578680100317c8842a4d31c40bf40141f803d61c081bf0890d6845be0a21331139e6d65ccaad25d3b715294ca5145a4b23208529b8b3e0779608c0a7e7c12191a9041dfcaeb2df3b4b047c291900f9faf1883f24fef72f05fc69433ddaa6053c2c199e7c7c8ea5d6b44857bd128d668e5880a892e3a3b444f03a472c400ce5e8e539e7c4de5f631c866133688c0136ed197baee4e69c73aea8dc0f59e59af597e974179393b46317a08639e860967137e86045c0c5e13d13ecb10bd03dfdc5df2f82d40a0f8de58997bca4e5d064e589e59090c63d58beb9b606843c7da294edfb1ae190e040818c72bd0b4866d2cc2a7b19ea8dd43cc9f54856ed24d7e3a0bd3e073062713294eb5df8216f5a4a296bb2d84ffb38bfc4e69abea94ae297d82216e841a12701b31b22bb18e4bb634018faf8649b31c12d3bc323cf2d91c62d9f3c5b32cbbf6f540c28322f71116923162029d97ed5e2968423dbc7117fd4c77496f24bfc119f63bbe28f6e92e567658247efbd813276198c502f346398c5a16f5a3918761dec1763c740e34c103b922c19c384904164acf30f4119c3302c2ecd1260f141a9e6079018443e66608b9914dee50b928588c58729b9b1f8e025b72e0c34b7483882c7f5ea2cc4f7d723334dafe443f9231b86a54cafb19194e9b32ebc30caf65b174499be731d65faeddb29deb8b5669f659f19c785793b6dd76955110cdb30cd847dcbbe16f7ecf653697808f7fbc99c653cf599a6651d0df41ae6a96fd17be08706fa8c3b865bf61cce9e7f72fdba6bdf4fdebe96ccded9eff4d98fe3e94ee9e9030202023afd498c0290e0138b25a83367eededa3ebb221d91ed16b7ba6f58b51d7b87551b2e726f7f6f3fedd8c5786e03e6c56e865bb6c57dbbfd54191e52bf3a64e3d1bed9ed34d0db73df26b395198b4166fa937ca19be478938d0b3930f44dcc5b13228d27abe445d9356cfd8e47f68b4751b9754a02af51a69f9fa5c26b5d22cce5d1dcba2ea26b9427a59276b2a8ca7a4a050d52a79cf7d37e3ee7daf93955b6afd06851dca5bd8a52266cfcc1164f63102beb6d906d7a65678bb34a5e8ba3691862904720f7f53ffaa63e47fb6a5a2a1782723dd73831cdbaaf576dd947336ba0d3f693d1de4b831c73afc23e736715f6f8fa3225c7d7172a39e69319a6e9d5ad1148349a446cd1a2bc215a963d23be1f6d49c3867dc3eeb7d96f88963fe34d1fcf7da45f86728d40bed686f4effb59f6edcb1e658eb8959dfbd6adf81881c406743d00f9d0c4957b94701ce69498cee3817854254a10d9842f900faeac33b7d8e1560f797ba43cda63d48e378ecbbe7ede304f96653d32fe70924631e51717378138b6e478a23635a7b62bd7571c59a2f499946241c838769063289db412f1729df42da4e73507dbcc3e3117034f0e65fac8c6d4be55ce54fb5c5fd3eeea1a66bf695fc5b21bd25dfbfceeef76facdf461df92ce2d7f7126bfab0db1f2eb9e6554bcaff2c4adbae3a1aed3f3d3a6171b01281c32393d7601b487b10b30ef7d9e7634ac32f7f53d2bdce5d7e116cd7488d5be7df76234b072ad7828c7ccb55dc14f5e423f89dc28e1e17e09a561955b4c72ed21b7e2e57c1c4ff27cc301465e613e721532310f17f3ccae888f3cf1295e0e2de948bf1e14b20cb37d77457ce4d6b4adfe8c5fd27916590df52ad6ecca656cf7b7d58f19cddf6c895bfdf87b9dd91366f92ba594efa1694c4abca4738f7c23db4e08761ebcac037535c9ad7e0ff646932f99878fe7e36eb7fa3d17c88726d979c0b2077e728663ab075bcaf53d3ba3dfba65fcd1b75994f2b295613467ddddb8953d665f0cf3c53cfd88e121f18d7978e21bf784b965cdb0a1980a262345595ad3ab7e6c967cc7cef1f586520fbcbec7de5873314baef80d306c1a4b1c450d800347be1125cb0698d46882521390dcb0c276f1bc64792e85c3042bbc78e9550c13a4e4d9da62df44491fa7e46e4094dc9f5eaee4fe7cf6d9cdd0d1d09fa71fb5c92d6993b7934d965d0c5200590a00e3b4db601d0d3cdaa73c67803cff75433479bb23f73e17fb14e9cfeec8bd7d22c17b9f2712b4f63ef65887e4dee722b9c7a89c31d8bc00e5f9d0766d74c9f3d3a28ebafd7eb8b7af1f130dac8f3108766b6750f66aa52701b9f5933dcb85a7fd09762bdb5d1a584f7f7acc6d70c9dbf6eda0e5eddc4744339dda64ee2d1e1db673ef3ebb21df0edbbbc37cad9f8ce2913f7dfe031efde9ded733bf7c994162fa13fe80975bd4e592377daaad6b2389fce5f862234b1b44e49a1c5f6c7861434965eabaaec3b8ed536da64fb575dd75ba765857a47b1b457949e779ac2ba2e5798b7fb838fc0135fc04bb042b95208cc3db7572c5a1adc79e59cca4e21fec4326f716071db4bfb5be1eb373ab576d94897743af07acda5eeba5c4aaadfe0ce5ee84cccb8ae36562fb9241fb74264840ef487726310d73ee3edd611ebb1d987338dc11917487f93824a10fa8c23dd0f39939771f6f67e61c0e4df7bccf5ca7a7ac1125cfcf9d1d479df3d971d36b77038f77b7dfce8ea370687a4cf6ad72af7968e2f10e873bce1d4c7d3edc79bce6f3e1f11d8ff97c42ee3b9e7dad9fbc0650deeab7834644cbb5728ff9b6c9ee06d3637efa7c4c3f1df585a6a3ee7da1e9861de7719d8ef9c2d36bce7d11f3bed8dd109e9e7aec76769c070ebd773e3cee83eff33d64e283c2611ef3d00bff90077e88ff3df5f98421f7eea18ea370987a2853f3f98461772fa4f9096fb0763ba6a35e3b24de77e0f0073c7de60b2578c2f5db31d5a3b08fc7cdee08771e18eb2cded901f385dd4d9889cfcc6b8e84c33ff038f71366a281a8771f130df48e5b36994a90e91c66a281dc6d47c3bc3cea3be9ccee93e0110f87dc3bfcc4742b16f3e8509fdd1ef50db76ce4fbf3d9b7433d7d3ff0380a87125401fb84aa873d1ed2fc84d7fcca63e7be1f78bce6335f98facc374b82a11caf10b22418ca760969e49845966a78ad54a6587c88927be488a589ab4b962e5732901c5f5d80d64823e35c61e34abec9f1b5c61b5d320d9296dc23c7971a55b21a5296f046de38ae7e2a0c17c9b2bdcc5ac69472e48229651bb9644bc9b0e2ca070032a26458c9366f260efb541513b1cff2cdb123d242172f57ee881c00c88808a612647a0d0e2518a680bbd3438c997438fc70d0c1136682c2b91f3341e149500240273fe0e9a7c79091e54ff8820ea2dee3a3f998748fb90a33f11ee379984987830e7ae7cee30b7fc01d3d09c83cad285b68c5971192b204535fd0c1f0860f830e32319d3bea335f288399984c8f79a8033381790c0e3ad83d070e3a88c24c6070d041d339fcc3e9cb972f5fc02ebe8c8094255e42b3bcd6d110b3c81277c0bb22b72a15a2ac433ccaff19ca596703f7eedec4e9705077137ed2e11f12009acefdf4b7b3a13beadec441e120d33dfcc484add877a7c7f007bcbcc4cb3c15f340e0073c35683a65e2dd50ba5c5e0fd108abea4d7f72d074fb56926cb1aae21d4fbe854396e74e53b285ee869b5ebb06783bddb9cf6e87bb09873544d02439cae3e7b60746a392a8144c35c6911a668c219a0104002000d314003028141008452291582c0e95791f14800c96aa4a664293c8c3248b51c818630c3184180008001010982919ad02a9650ead843e9fddea2d6141b22cba200ca710a34df86b75054f8b4b534b29509c87fc1d1e7cda2af627d19c15efe32733f74871a2be0b55c1a552728e0eec605fb8519282722a414f6ae02c5e3198d7f0f58883942e4e243003f92e6911a4ce19d0c72301a8fcc6676acbaddf26070057e478e843cf79b81cb6427e2556d385cf23deab577cf622d7e128dd5de3a73fa7bb8e8e3885767fa392a6e051518a3c3ed3e0e011d4878babcf19c2f72ffaca81bbef50d5e09e3236162ee5fa5e454ccae07c8be0b47575d4015decef09627a8b842b445d3d380dbc91514db9e8811bd53dd4562a555ee32c67347b4073c341ff21a984c1602ddacd720b04d8f3367d4ecbce4cdace383f315c5a3b5abcbdddb38cee2e91884143220300eaadc9311da26f9437e4aaabb3f6fae12a951b88367f68d718b2177d7b8f2acbdea1b5bbccdbfba0297cab8fe68e8aef3536c9214e1200878f97d192d1e8d8c64f93aa3ded5b2cd87bf0268c6e22707fdd53a5dcde5a73093e61077960b23b2a37ee4e68f4f2fc4950793492c8bf9f657cb67e6e8b48a3093da1b62f0c281058a65d211e132f70b94501298a0ab12066b81ce8574700a690c0afd558b934750e5544632ebca68b2aeb3c8870e6cb73340d25dbc6e723a25ada293ed67517f3a75d270f4ce4af3c85178af9fc6819bd06845a2a2dba50dadf79064d078a0dbf1d48eb81a27f92221aaf891a1f1cdde264ed049e0e06aa37b75a371634980c4d78a1d2ea45f41ff503069bf0f24b34f8d1a1b9b9d43e999ae09aa02f9f7632f8f81c8c8c7f73e752de2432316861676ef70d977c3c5347a4ef9dd3429dafb224c01faa154ede2c495aded9f2c05c870375911c28c5459cc824d8946e081aa60042fa8b54f2d9243684a6c556e368a5cd74d99464814d6ee9231b51a14c104f52d4ff43052029653945d77db0e62b988d80bc9b1ddbd16dc43e651fd71728e7c46c30d25ccb49a055752db7bf99e9ebe73bbf1706078cda2c5e118df22cd5fe0b98961af7a8c3345af98fb1409033feb53262e0745cd258bf48af716a9774962c718777f9a81054571665226e04600709d6a10e6223899fdc47d7693f8353f6f68a109d322fcd7b021521b51e50b417ce0392c59c7740eddfbfcbc075e7e2fb494cc77af2dfbebdf3fffbe0ff84b5ea3fe81c866801ffcda7788a67c53814220ef9e82b0d14ee9a481844f4f441e2386f51f33dddb29f0a37386ad2d76841fa99e2a5d5db2499bd562ad2ef4988b7e3001df2dbf885a7f3f2f71dc62be77f0e20bbe9dadb601e3768a68eb5b6dc189cde9e6aede1349eb0416000f74c81c07737a24ec2b746f252829b835e12000d024bb34878b0b6f412359934953da0f22cb86ba78df20945d1ff5d8d6dff95003ff3ba1db4bf767dc4c7194aa8a4573a2b283f399aa2835ff31464f3041df6c34129b2315340a1446b6ee2e3d9145c6e877b804c379eebd91e7a0e884faba849910408ac2d184e03aa925d34830ff0ee9352df13589ec846db5b315d7a51bf472f91ec929c620d66850c9cc596210dbc3137396c82be44ad589d8d41cb10de3a2773b476c232be7e53eda288958c65d327fea695e0a4e6973cbe0183b631ba3346e5671d808b4551282e74d53aa6aeffd0de1d33729a531dab19092b2f513d53665e435b3ba8ad4978af9d900622218d4950697d08fc23f7148797ad8e50b42576e4b92e66beffb7883350d615eb10f4294d89777a4b84eae1eb7cede210b6bc6addeb1d696a4b3d1b13e4f9a55c6d5b77a922bb2520db55cda2a2e70058767fdd04b93c74cfcd8e09de4fd38669e53f901f751c4937ca50c1b434c1b2ee1b90ce89416b1865e4cb18b1b71d1b2e7a85dab4a288c77e49233487da2b0458488755c7bd332d194e17e3daa0298d9244859c0ecf28acde02b712b216e0e51b7ba1e6449dd83b701e05c2d809007d52e91f5554c94b082acd02b6dba80e7211d9215ae24b34c49fe26d34349514c5ebfa3208724959413f327ee36b30b3b4201b12ccb196beced77a1502de48f8b0637776e90676d449a561c746d96541203a96d7284548ee158d022f147fc0d6dd0f72dc4ae040735341a7a9b602ca9cd7ba8bf89bbcbae5dbaa47ecd297307679b2b8b3f3d825e6999256c65f86b1f06fe74910eff31a369ea870c4cc3b917ced6dddd23866af1d209cbe56b4d12e6db824d7170a80c1fc25defe5221bce55393f415f1403218c61e28e10ae8dc1bfb6031e630d3385a67bf1bb2beab7765fea18b1b425312d615b4490712a3a8f9490b6d07a604d5a89162d0227f70bd7114e0d67bbec69c34f7247697930640f96d102bd39e0ac98ad2fd40544728a0c38b6f6cf4d4dea0646d2088f200c91c8a702e7b39665a7feba126b8f927200d4e7cf48c5ed094a8e67328396443359a98914c9b6de204ebba70cacddf31f907f6ca259617f5ea0da5e21485ddc893e92d99656cd178c6fd67cc516ea678ea667dc276781de782becf5db12ebd8d1eef6aca34b0af5d5dace0afcfcd0e8e6adc40be485e379565427f2ca3699c13822ce583bae067b5013263eae4b977b7819b3e75849df74f1808e6829dbe06c32656a7aaa56bc41e500e17de6b64c5ca514d33bee0039ae370916b2f7e19f04078058376f624745815cb825c67f66120e0a2e68dfb7d79d509f509479d2a2458194653e07ae3ce60b7e956514fa317a9b251cd46d461265d60ac23c5b5ed22a9ed916a9037394f90b38d032d2fc400753db1749b547cc503e6f7373a8354ac1351669dc8554d0b6bfd7ce6194590f0404e5cc242df0d1fa2c9f71a695a08a232267c3b7a2d02d9a8ad65371d326ffc5b14f9cdb0424d8702e1252b3cdad37c6805e1aa732b0c37bece5554d533781fc7059160b1c8864f0f0a610b43fb6107ffa52ac437a702c7a31075e6082c66d7d92cc959bcd9a146efe8fa979d1d8c5090157b17356bbb21f3dd893dfa019446addc4242e2d1274714547eac53fc758631e152a46b44eec9491d1bde94b53d0342a9b41ec5f4e9f410b51b3d213ceb3966dc89e7be2cdbb9e90a033765decaed8ed094c969055859ef48b0e4f2085a90929960a7a08894e353bbff7c8dd5d2c464ee54c0020e18909d9afbb40f6ac87b17eb8f1ba6c39fb8e58065e2da4ac8b43701f5e3741e60df175e44a8f848007b444e84264f8e52b380a512f936f1dbc102332cd8ae304e28d8db6ce69a6db44e0f5daa3e79c2921f9a979f6c3bcfaf7475a7fc8b51a1a3589a1171187fa9aa7872df1cc57e6d8b0446471ba20e472bf8e00b3fec6e697186c5b8288481ddeff12e0f9f95fc1708ffbb02ec26dc3dbbe68b09706426c17f1e1b1222d9c19e7747d89a71d31a31b56e3fc1a89e4421874093fd7af043c3dac21654e4908c0dc1e40105d6a3ff31794107ba1be7958d57dfb8a2a3f8625a81b6730bf2e4f2c50a59a403ed1a7b076f7a2983cac3a614ddf9b251190af6688f8afe09b52af52fefec3930b84c3d9e4b228234afd0855aed8cadd329c809abe21a990ce6bdbd270beb80b70da440ffd8508ff3f6b5dc0d1557f27cf8ed413a97f037c69ad45e360bd083c2ccf6724bac0c0780f8ed36292e5d45a045924b86e200384e2487db37451817e02ac2d4b9b9dab58fd6ba19573bb32ead29ed528c00f514f25f57abc0d4545796d57b765fbec8ad56e2c511b0950711762654b13efccad070037a6a3ccb6923cabc560b93bc341785ec2896a978db6d9ec962bfbd443a18a8025aac010be503bc5e3afe8cb0eead5a0403236c063524c9b019a1f4cbcd2eb82459d8abc070ae9feb92ba5fcd769d61e67184934b737d3e06fb503e7fa5bf76d03618db30e432b12295bc9053b3249108ac24fa243bb27b3e0e92bc926235a342a5c2827adbbe464dd8c6cbd49257a5b7efc94448686ce92e68a64ff98ce75b1ca7c823dedc8b0b0036e38b6de1c48dd335887a079eecb3245d450239fe30ef651f92d8fdf3af8f298aa1a2a0106dffdc553057dd3bb9d6b5efad94e6eb65782772ae4c31742e965bc42c541638279255a2f29b3be6c1565dd2fd924bc1be32d685066f4277328d75969262493157d87a141768552b502a669e88a2be689f3664e32dae2c5c71029667ce86195cfbe0384ce30284944dde1351d7193e494122adf411d1014c1cc6bd79706978f5a7c17e48d4ab50eb5ae7e3da8eb8a932f332fceaa77e9e80fd0a06f7dc5c8c7376e1d9495b0e98dccaefc466d5aa4c15259f2992f186ccd5d331e088aecd50bdc26121e92fa86d03433a57bbb62a09d24fa4962660ef60ae3436ada1f0fd7b5e193dcb1443a072589240f48cb9fea9bd151334a1f03a1a0c505ba58901b8671877cddeae11d8b3e81d9c76f3518da9c5db365c74c4ee9ad371eaeae2806070d5301d5a13d8569e6b76bca28a5eaa9f23c61ea79c7d209cec31851bf59b3e7f472abdc0288feea0d4b6d33691d64bc5e962d65f67f298c799ac8b344316887a1d0d9e907d6aacbee8f5b5d06f8c66e2a3402f83bc520d03badf88d4e4e42707d95501679b4cb234c2e0885feef82df354c393c81b5aacd5354adfeae0ff09aac0e128557422f676950a579cc8449ef7244cfaf8c58dd48326e22661e06c4414ce362ae03faed44caea93833477671c294143e11ab8b28f225689820df7fc7fed289278471da030c33b4dc0821d06f3371a0f38e0b2927b704ebce0b49e67c0216cfa39f3ad27e6f0e88063e180297a8376a18698466583711fa375b1c7a834e703d0bdb5b30ae9d074e805c3ca26da7bb455ac9db18aed3f4c5aeb877a72ae3db38a8253cd631d674e6156b6fbd443503d2bb4d031053a2360c2af4b0c22415b9d881c6aeaa71796c5b2cd5b5a0001e6e96ef0f012876f679eabd39abbd6ceddba1124bb061c2450fad13c91947d3680850ff548b2aff0039428743a5e82c6a22f6c0b53272bd54505963503bb55bbf9cb4a1bc96f91862c3d8b2c3b823109c90d2cad2156bd1c302b7e01a6bf81f37d1cd5c41f52c80dc54cb23fe38536845598a311cb5fdf527f70c64d505ce7dfe30a8d93ba0d59bfe464ec51d81896c121729908d5305d4dd92f71308e488ed227d43597408831a39d6af0d0eabd91c3833565d5008c41a15b997ac07205ce6480349d3733550b5b8a66ca39436d701d1dcd020fcdff2bce08e8bfcc193631e0eabb9f39870a0f1ddbc84e01dc495a239dd7950b4b4d209b91d3549ea12f031187221dfc5355dba78b304ca46f1489b2fe7ca8867cc0da6ddddba4fba35f465435adf3be210365f1002dfb600947a6a6068ef906667e813f6b9a659cc7e7838fa00de959d6f1a0243db6069c3e459cfdeb41167d44a758f8c7bd671da8dc60ef468b5d9949c7497b9e8cbc9b0c92734d741d7a2599f90b9b9681da7116a82444b8217a854006d2b6dc5d06dfb3a2617fce000054fff8cc720315276bc5a998cef52727881ceb4ea5fc28cecbf5e5f9e0c9bb88be369bb60c323c51fdab623966e7489d5b76488ccb70e891697e3e5403b11e1478078c32ee0722d3eea21be7afd386cc2d941d540616c3600c40802c0d100c834d245d8a7466ef983efe4a30c2646b6266658bb0d3559f00e5e1755483d6e3b86d58683013a9ba38ab37433be458a211eb675a26973e20845ba7c7207537cf10ad6b10d18c86ad18916a8d254a4a7319a49f1f73d602ef6c6212d6ad96f37ad509c66562a14c8df03719d6169165d87409c49a7c65ec11999acb0477d9f5fb305fd24b69f09e4fca7aa4f20a1e0b61d450593e9a58482c7fb7b14665c0e417d161e852c131c752424f7dff90159e313dcb34a945da3922853af103bec2a760ff5972afa57d2177001685534d6faf1e26bca54bb6a11c714573436d2d91ce09e68f4717670d64affbd3aeeef53ec3cce7186b1f77f89c1d8596c5530491e8ecddd45d3457b2176c1f1de528c1282169d4c8631eaa4f2ea036fdf19ae194ee23b76b1fbcebf105e264e25e2a4e2b6da45c929e57fb8178c5f7d2fbb4bee8511133d8ee8a5a7cbd55877bf5ef9d37b1c2b669fce67a71ea136299887b03ff4ddceea69d1bdf1f5063db5df20c1008550c9c6e7d3a68871ee077bb7f1cf9591a6bd42adbef3af46cc3da0fff90c2ea82a19b925d6c2f936383384d6d94f3e0f97e2ec0a70ba0447c01d717ae4ad04a53aa8dd9388c96c7e9363b70f98f954fb73a0d950d39f6b2ed802de1b71dd57554ff5e702150064b432f45668551f61f93e0658f58a23f5b991392ca15620c57dfff45519ff5aa528105b822cee6e6a9d99494f9e1546c9aad0b7e31d31ff4281635d0e642661126d8238cea0cdaa619fb8d1202f09bc74e241ab8804ee594798203b1ed8ab9723d417ba86d9cf517a1bc337ca7942f7c43c7e7ca16e3d9a7fc7b494ee1b80390ad8b83bc36c92ab05c07cbc51520905628d97a0a76a899a6736093973897ba1009282f7fbb0e49e8f867b8e5d184db8c714ec80348945c61f38d0c83d606cc1d7a986e1ef29d8506c376e15531f1ac305024b495a57913a2dc4c380e9408e0b119c6be76d20f66c9b7b186b5f03d8b89090082e37acf0294de800950ad2096e18bc13b45ab29bd09274364b0d1024977e60598818e817f284735171d38c63618506fd9f5d4ba6541c09f1e35950644ea1df0c0addf5d01b7c1d29c4b5cd64cc59348302cfa0e53b9f2a32105c4e12aa411d9219239b34801f3411b255c11f27faa20e6517c72203fa7f9cc4afb7740e953d3a4f874f9e87420206a305956ecbda73763382ae215ae8af2e6471290b0a5c3b3d4ad5b7d8d465f3f8be427a803f98125aa70551014938fc3e829220a7a78a71896624a4cf5b04ce02a453a0bd72adfe89347324252af3a1b2b1cf8d87b0f275f664c1da854547ff5918fe1cd1b0e77f463278d0d3cc6f6ef0033b962fdc4bd539f2910afabb0ca371231d8619d7c7a02cd11d37908286bd8bbf8b8c5176a81ce40bd8db6a51ce20699d9dcedf5bbf6f31035a9ff606fdcb68e74311881d800d8e8027e99b158fee6791c71e90968772fd3ae6c4923cd58f2a088824fe3125583a7674c0cc2f47ba4e99396e0f17017ff91c5f254a6aab7ec05f66c41c48ed869ebfc8ab3c000d6847c6c8334f37c9e32e2f315be0513d9a4ba9e46a7ef38e982ab01b1106d0ca0b12364988ee8b0233b55b934030d22444a83372218561603a939c2c13a52788bc5064ad20a09b5e96ee52586fd2ff95230a96c1855c4bb978db7c2bc4caee635472a50b2c67b2c9c20e4562ec22168ddca7ad5908135c4d0560e594b36272b6627578d75337361b5d364bc00ca589680241a6e900651ddcb96e2e2e53f8dc096d3c3e39b58e5f0b55a5840e686c1eebd2cda4f448215138c91056c3f1e2914ba0732ac0360172a77bc8231531ab818c1de69082d26fa9a33710fdcdd097cc4b29df193925159fa02a022fc82810022da824df499eea2b7ca42d33f4c4d7a52f33d810c142419417548651f0df5dcb0cce5fbefbd8210c6b892430e3a551d64062e2bc1b715d339972a0c2ee0aabe736888551928e5a77762bca4beeb4806669393ad37d298b094f9dcd4318dce90a3f47b2bff288304a8e561443329b43d0e73b5b582d63f1613010f15cc43b645303867ccbef54ed3e80ea0a06df34bddd8b1fcea54b6eef68d798353c4ee6f2a276359f41c981bde4d81abb66693aca3b8d5c2bd37be5443eda8750762c473641ddc43acb9590cb6c3c4567f305c2152fd62b66a42324c86bef68def5f6c0e541e8cf1361bdc604075b251a775faec49a197071f7775092b7c2eaa41055d4e673f54b149807fd357b1b841762f6ef6caf8ea15f32d1a75d703030ac97017a27bffed3e66abf39de42b7e9fd0ca804697a3cb0b842d6c7d1df0036d03a2c83ddb340ea003a617a9173fb382dc7f08d54378df41f33b2b32f234ac5b3e5dae762ebe73950ce68317794fb702572072021e230936ca21a55a4507665a191ff0705c1c656358940b5f79d1b94d5f73c03f644356c230a725c05139dbe1ad3d85eff67749285937beb09ae59998144680e178bf410d4405dde1d159870847fa8c5134ccd15d5373c2c3b6f764202d25b66209aa35eaae7e53bce77dba025de81d557a3b31443f46b99a659ba7389b5df37dfac0141ce6fe4f5b91b95e3baf6cb12d4a7667ba45c6727ec9b2c7f1f02523130f85fdbd76c307e6c3d4fcf803a114ba9a9e19587086a198b4b2fa76349028e01bfceba1a15982deb4652ec1018e2be442122da903a21a00c4eb0b3bb951464a71ee41d7b6db90e4bd1c9a4837302b7ab2911d3e884613c011ff80ba1f5325821e592f075a3d3c19a02f18a177df6447908ab2c9e10f61f596b899b2f6897889cb53adbb65cdf568fcca526eed10f56d3bf8d880fcc86eeb9ad88affd11409d14268ad566852447a9249da46824a6748040fb05cae45ad7738d6bbb245c5fe8e450605128a11065b61d47c8d4e4a794638f2d319b733625c0e68d0392732e0a5d07c5db058a98908e4826157b55dc8213ba7ebdce066ee10829e95740564a56589b8dcd85323175877db384ee63541658d838de83158586f6253522fa986f9032d796e00277fe4936200c811d1aa1ab59e84d0f2b4b00e9244b00dbd1a7fdb15eea02d2f7594f7ccc13841e281ed0b65847ff6c42d6509b831bf16ddf08e85eb04552c229a7661dbed9b4f117f9c1b1e48d5cbdcfd2069ee162fd40283ae7f9041954e2db6c7212be717bd3e5d25d0e7662e11bd97ec58c6e145996abdaf04a86b29650628fc5b8133d4c22a26c26eb2763a61147ce839ecc17b6bf414fc8bc91fa1b4c848a211e33ff4c207c632e8841a2b4067a8950284349968dd2ae3e13e8cf835181b40af328e33806cee366158cf5f97420322a83860f98182bac7051756d8852472f610fb0a563d43098ecd5f534657d3b8fecdd1dc2b6c7a316c5859fd093448db5f3d059c1c71971fc5f921440280e7c77a61aafc05d01a17c931c190834ed4034665770a4a1e1607134f67668cb2bf6e46e8cb84e99ec2e71993ad2f1a772398385305bc91c6a1d886048434a96ad65722131482200c0948ce4477b55a793940f46083af91b357dc2564b1cf949ffa9cc3b9fd842896dea060dc1b63c458f0a836639dc9a2e47fdc5196e6db1a81a8cef57c6567210aa42db6e3b4598b09c39dd4104d0e07838a0771a084edd247d4237576eab3417567e9d61e8854e3d80f2009e0522b5467e54e2ae1ada12ce80fc0a1ae385f425cd980278d21fba848c3fcb8122dd10bc36767dcd29469aae990de96ac25d38788b2aa67953234b1729c12788b94b4a9a075e8c8e85d79e54cfb716b9979917c97a2d447bd89ecb97d78aecc004af488c888ca5383299d912e451af6b237def03eb3c3887b21aa8fe291e494b4dbaa57cc40fdd96fea7a91a6b89d993edb56b6143af25fa739bea3610e42ae1bc1eaf25ca5127cd050590a0b7c63b9c29cbe53b8e4cdca218244cfceab03c707261b07e8667d7a2506cca458bf91d68e90e1a792642755ff74b3af806b084c7610c8732546a14203f0cba1cd6bd44da71937b5f14ef82236636d41da0bcbac16be411df7ee8bcee9d4b042beab81a8146c26f04bfc10c00b984b4c551d4e0b98ec5c3858d1668e0065ba9ca35c562743bebef93d76097db2a60694c80fa3cc3eb3d1384409cae906ebe235203f2499af51b9a2e892debcfdcdd25c86c882cdf1a119a8b5b44acbdcf4a6a2cbfc69ec2b6eac60a29ece33f6ed8c564986d239e71db78124424628649862835ce65c3ae942efce7f129b8046df7ab320101858f5043df98b543d9b96ce1462070345e34ed544786b505685f19593fec78520b32c1c104f7b2c11c9d2dfb3c8a779211da1b1562b83d6f2b84e998c99d87055aac36c215408b91a480e8b48217f2c1c64083939ecbd9387226a4a9006b406e0947766cf9c33b2fa2bd9bf337e17bbe7d7cc1f9ffa4b22716c020d9550e9fa4afde81240066b84bea489292208629480ef7e1524f8aa6216e08a919c970d161f1369ff056d1d7a0bd15c0e7b2edef4b792e41ab158412e2d59821769e10d0d5d79d18c9ce03c9e1c58bacc5eb3e4227c17742deb99175c99dcca5d97304c9621256c9bcddc3e4f5c9d548d52a2c28655566ddac8fb3eff89545602a7336564f66f7613b95aeaf1c6256de6a7644e976eb0f0ba379139ea14870e04c092bcb0b13c079764c2621319bb31e2467ed7a741c7096ac13eca390c8b144c6711158a31a8ee00256b4e0bb2d89011844571a1f2c79e2d2678c748780823c79e9aa1d6c5f76ffc650f20e65889834ec332f1ffa83250ee03c2c5e9c058bfba618043465e7c3229ed643c7e32b30ab57f4515a57a6c3870587796038556085a5756d437c298bdab074e23dff135ac5f2315032e2cfed5f21a6c07e60856a9cb2e441c3b3345d38d3b3c844f7ff6cf05581cec31da28410b3ae1caef945ec8debc63ca2de8e20f526362122cde744db385c061f443be0a298692a7de0a0d62ba8c2eee05926a9888fac08376285c1b901fe39c6fc11aa398314a3b88cfce1582e7a584e25207f2710bda0377960f121fa97c592e13c733128480a1900c3bcd21edd3db5fe288e7ea2791598561038ee1cecef5108eee7d78587c583b4f5bcc85257a79d99e1dee2583db4b007d43a9cf6dc2f627735cdca1b1871d10fa81fa5f6e80fcc21ef302c1b15708a179b83ab8fdcac880dbda337b9113d3dd4c12d12ec98c605e406da9a6c04485ce82453c8ced84fd01b6e62f804f3e789603c23161e0b3273ea8715af8753a6d2a599d2922708f72cd97fec10c2a8c16ab6f03c9374da94b7cc13613d27761e13f45054b4ca7238cf264d9b685a63e2f1ce51ab470e798a16adb0128e324b674d4bab3c618c736ae37181719b9e68d57e18cf22bd36695a633634649021e79a89bc39d76a1a35ab0fca4199a50e9cf11adaf65c7d8122cdf4e0a91f0177099aebfbe7f8b4813d81b3c2ee877daa24cb16bff38d530bee997305137a940018b16a9b6831e186390730a12743888c3be6914ca8f421ee5384a08ab2b0e3bb5a80b72e1b7f51be80316eda7fcc3b4d656b1c3f502333a311c6b584973a2a5d7770349d8dad782d630c9db0b162ef33ec1c528fdffe9d59c57fdae5d01d80daf0925b9b9ad5c36d80a95a1e77a1c61e621b53d066d7eea8e37aa2cde54a33a4c657b52e2ee85bf4dfeee676ab03d4f89e233a8c8b388c572af19a8c3da4f221f09c7a19c5cd5dfeb453a0f3bb681978992437e8030a60f5dd7301fdbcd754fb173b87e94549969d7c773c2ae3a014e9558677f56b122e6b52bae19cd011625abb79de5461696cb5952cca69ccf415e2fbfaa9c26e78ad3cddea162e819833c18e884a64643a4a444cfb58defbe444227765768285c7a4a219018414f42314b3e5a8109b7f024b931a984135b59d5404053848175a9f7411709b2a069108aeacc19190b8eae462af6ae04dda269693ceedb8590396f28e6ae692f2c8851b4ee9d06d1ceb870def04a70ea3082a66802bf0ef9cb252fc9f2314c4adb15f9f261e9bee73bb13b2dc9f2f12936b964d6fea29116159f5537b14afcb768f50bc2d686b24516b81f87692c548d34161b60f87762b88c96767528298263d4c9b33e914d4b4a1e4f3055a006b76b65caa8879e7b3a42dc6bb9ceb012431c7b4410a9c2fdf8d582858634fa242e1487807adbb30917d5f6b461ad3fa4288079f4160651026bb218093c6203b9c0c16e16c8711f144571308242ba7a0dc4cd4b9a8fe8150db576bdd657f3a5e7006b1bbe2cd9b729f1caf79ef30b06341e3e283b0019dc10a83a63f578cfcbd033f2ea6d6c4c48ee15681c1e82abc561376b9572b18c581340f0b6758bb5f1a5defcd590a081d37b4fd803b454bb9dc178e5a271227832c87a8b7f11f6f0ac559da2891d68fe756a6b104e9592a9a4072fd191b137a7ce3aaa514cc174e3480ca520fa5e963497a208a20c9de7e7d4c7dbf5c5b31c0312c02b67f227b8eee6abacafd5f363dcc8d820889a14face9c38a4dedc10f1af2d0b0f950843d9600e87bfbe31862314153952228ee1cbcb57f6aea616235dedec23d155450b4e5efec214635dd6d3828d24a60c71b55697e3ce02d24a139dc231084cafc69b9fe1f4e4ab00210c25c7ba3d53532e989006c4d06a756e3169697897a7a992ebadd657c0009b48eebd8fcd55a2f40b82e7a29cf1c006c89d1c18000f0cc541a7b513281033ca02dcbcee0cb06ac77aa77067855cb8575373d0dc611143c6b3005712e72cd0623c4f63933929aa34704ba5c4aaa7f61afbe8def893a327f2c0581b23190966dc238a72cf7215305874950531b2a7759b5923d2cabb1f2dfecbef8acd100433c62fd138a24a8c2505956126dbfc21ff031eb55c494faab1af8b2c8d0c0c18011090ab45102c3cd27e1c7ec3500a06e9290d3d1c20021f92934a3ecc34d2f5a838c718ceca126d6b53a535e33bb4d2303a05c0fb1bbc141b3d44b4d5520757a2a71d2a4facaaa89de265bc1e3827e74189460231086e699ab6125480906a436920546f2ea492a4921f12054d13ef4b02b3584079eca37e663c889188196c236d10764080efcb29a5f14adf02f5216cd9804ec3c12364f02b71b7fa82857078c226986339c1680b08bfbf906e4f503e83960c119957c4bac485011522d31fb34bf15a28039de7e9a8e3a9d8f5df6df0adce4a2c88d02a0ed864b6828495bf79736801439653bdb3efebb670949f41d66a180b4b9ca5608ec3276ce6753db2ac491b064512a01fbaec5297ca247bd33014fddb68e0ec7eb5f023e6890f65205d69a3f1bb3fc533f86c4ce65d9e406c89426b9f40d981ad4bf98fe9069e95f345daf78828af2b934eaaf62a7cc466bc5b7eb5f3caf158fbecf338d1c3e53e31e432db0bad07df744ca5b5cd880855a457e3aa617723ee40213316d641d624dd816901ff89f2b61c08182700874640e993003e42c2872d4f387737073c83cb7bf1d73e9910a3caf5e267956e7a74e7bfa4f343c4457490955bc9051cd88445c678bfec3a57a4a011fa3ffe24076c12a9a13169065777b00bb4658bc4d71f4bf71d86f9f014f47ffac5b8c45ff3da004082948a3dba41de4e1d77e7bf6cd24af520d3bffd6c245516ec147a4b9438b1f233e877f4b0239ec94b1eb4d39d02bb47a3e0e4b75bafc3c37d2871fa03ad6f42057e8835b80d545944572bed8424bbb85fb1ad37e12d0131198205aa6e17ec00d9ad78be66dcf9538beb44f66ad9a162d00634866118e75c54eed81bb6a8d1345beab0e901c2df1232d7c7647b6b2d4908e8296bc42d8e4a42d788d14ee9c1160ec7a7c24a64d2b948e795e8b7e639b8b41e2cb8bf30f39e89e9395f4d6b5c9b14732dc418ae229fc056502ec8493f5423e9e964b4fb6389534b61ae35f522c2ad15ea752df6143a1b5406ee0b062355243bf1b3d1c9ed2030d1107a9e6dd897896d3fd085d27b34ce7f1f5d8940e5658ad1b33464f7102641e4761c14b551bbae2d82a1f069e4380bb3c69bfecc7af2f715b0c6b049b501f062419429e111bdfd1846ba988217ce5658c1857578a13265f1523dd610334386509ad8234ae8020ff3e5be953c084c2644905de5530f891a62beac380ade40c329e69256ff6023145158db0b6af0b91c4c36deb2dec0b7f588b50c7f05245c7e1c5e41a905f465472593659bf6aa0f2598c259a7d3296f9aac10bc9db2ccc896e851d3529227253eed14d41e34d11b0073675e18653ea8366990d2d330c9d3859af0e75c1444a2c214affa38c0a45bd91c84282225086737c09ceed77bcacbb707580155d0b3487b07a6e11dbb2e94dbc401b199ecc68a36cb8cd460289bc75fb424629f7059131ead30ddacb3965fb620bdc4d4aaa1ea6c6cc72814d2c9c1328f47465960f31b5f89f014652be5b86108ddbf20448537f546f5fbc4f70b3219301e3e28dafb884645ad24604b6519b94249a327aed5b78e350dd816db75b5a223dc27d9e463d47e3df81c5eb1c95f355d2708e90152c6f74cae47b6b526d01fab2a432f7532e0286b9e2fa04bab700bf32bd6597eba91382ecbf75ef2b77fcdc2bcc8ec5d7969095182320796a485b9c7d52c8f91a7b7bbef8c24b0c0fac0587042172532d5ce3801a8828fa0ecd32c45cffd605aaf670b1bbadb4f3a1492457da9e6166dae26394b4e816f991fcd373fb4acfdf0e828a369a4575f8daeda6d596f8b41775e3381258a69d4d1cef9ad0e1002c05ad5ce31059b9490f6cef63d17972b1e92ad454797658034b1ca9f01c4410b7f2460ae00ad380d3ce45d124d9100bc8f4b9872880bf61886562d432c7fadd00170ca21ae56735f27c4ce9341e5f6eeac9be405ab7b14828a12e9696d8e1c6c0ed96c1aceec6917b309ca4013d3a307225a62f9801010356daf0fcef2bcc6fa5f255a80823ff638ef1e33c90ff41fb7d94f3e0dc8ffedbdbf9160a624cdb0d3e687ef7d3f624b010b13f7ec5eb8a7f0d466c3f9671212e83585283e53a72fedde9c18e975d06812b16840319458f17b03aaddcbf2b3a2b862b78b4fb63b164b76e775ddaad2cad2f0d6b145ecb0295946fa1ab9a3e59cf0af0948b7a38bc6ddafe54da2b21ba3d1b83b7dbaf295eeef78024585cd349f4c30cd3ae71c700d3ee1adda893f627b8fd30145e438db1de5be1d56c31a29494343e6cd5062a552052cec5ab6f475cf38b5da1f8395e5fb0019d09f1f7cdc879f0f26f0fb259ccf109725d2098e232ac74e371350c842d169c2929bbc01ef25e6d604177d58179d1828212fd487e17a0f34f197e1c5b7ec514f0517ca09ecafc3d327995cf8a6e6c6b0d0cc8f61d0fde33a392a7825857663e9af2060704d23c96e06a40007580c4074d42bf4c7cd3fe6b47ead1b43e00c96d80db244f0568d2d084496a01606e63dc26fd5700270d4c98441300e236c46d92af023c696cc2a4f501406efdc1fdc6dc326360a73614b63256f2fb4560662ed11d98e209f05a12109733fb67d615c188981222d2f13ed16361da8a1cfa6b228b251648d6ca101f63f1534f8ce8b82bb90b17944452c69b26d228e351e84f12ab036e50858fb835b02585a07074921242448ca5afafd61094692321c47620b028fb4c4bc6c3c14dc4f8f34d2d6c72921191f0e4d50f06a7c5b54f490395b9de51d8dd580cf47e5d99aad0aaa1eef6fe3bd8c820de61dcddb2ffd7af13eb16476dfa7ffafeebfdab9ee4a8ec37cee412d9b24b3805e43b49397d888bc6b720a6a6f0ffd88abd732ba6955fb16addfebff9ffb7d204dc4bf088d029906f3aa930527b5d7ef8ff23fa9e63c2038b16df23f407f14d5dfee48abbf6a2aac5f609c042fa2a55459fd866f7fed27ba847e5ff2692901c000f6ca795d1a1fef718023f62b5ab9a5260089e0eebebf31640db2c74db60fdd3f6a003eaa4ece39773e7fd73ac8148434843d557839167d400064659ab8475724ba80df6c1ea5526c0f8c7f00b90fb7633c47c83c0f78b72d337711b128b830c005f02ad7b43979fdc0f09271f51c7cdb0379646a8d40b6d3430e49341f21c4c6693926f9b62853b4223ac8e7c41fef76ff6b3503a918a8e1d4abb2a958653bff6b2c2d672092ea18536469ef6d3e57edf131295fca0f91436c7281a45270f80486e57f04845d127e551c1b8ac593512e839ecf46cdb1130ac8444249a1a8b5f82662ab188b7374f56c1a244b306277945ceecc6904d53fade92e1b0d5cfe1e4ac78679df7615b108615831f81927c5d562655e059c82acaee7bab30c415a18aafd92fbbe50c50f0e9f3c2204ce23932a140303fe9b98df820a1cd0d72d19172f98b75e5b4c0816c46438ff47bbfe66d4474c8b39b1d48c3ed90ecb9376a31346ae2db642825c6c22358877b0c6fbd342d13f5afba358cea5f948d0a1b3ba191faa4a38221760e39db81d0fe69865a331dadcf0e5fc17dc9f422b358dd6521884aeb7d9ed04882ec38064d7755bd1c38bb8138c371d91848ef4793288aade2c20a5ca9e40dc9a66c6256fffc822cae9d457ec9e3b0f14b067e79f21a73f47281a705516670eed7a02c67c0639189b05ce4a41f30f81cc8b9a5331c74863d243437ae67dc544dd49af217faa3e72afbc70abf6fbc1cf0039797511eaa5880fb43d11a9744e99489081ce0d9c093b86f25e6af81936b10e06d0e787f1ace3ce39de9f994e8da9a64a857a642d53ee64d845d711b7b91c1425039018c6d34a645eb7128582d4b00795e4aa0328232dc4b890b62baa06eb6c7520525b564c4b1b26883d99c96fd9e7181bdca9595f795897e674116d06dd0f192c58605bc78c2d6eb48190ca5b0a2ff618b5d4dc53d0eb1e0fa253501b18ba96a71c1dd7e5ca9dfe4b26fbab91a1c8e0d590d993e415541ca88d4a5aacd40a5e5779fdf089341c29c293f1753085ae32c25864ea626bd2d9898946300938238b2364686837fb1d86afd031142ff02ee1171790fe0aadeb1a1de1c4e858a99090221346a464d262e5fc879656571b3c8160c2e7b75866856de1a8aed470b42d73bb5a523ad819d9c42e1b650af04cfd1540f043748a8551c4404a97179741ea69ca0687085b75784d8fefc7730ecbc8aacbedb170298e609c073133f6a99965187761da024c15136ff5ce9333d1c1980b9de86c0aff848bbbd0cf0be4f23e143898bf132364bbc8bc2b2e06fa4c5ebc150a8be4e21825f55a39aa75a0899aa0b18d29a65c459a5f2c9f5daabd4d8f55962dd748cc63a755c1bdaf1cfe9b73f78c383f12b95c0ea55b80b07b519a91ac8eae5509a161bc1e58e01de33d0aa07fc93a5f9e888491acae7a7535847d9338c9f9d73b94641767c0544751cc4b5309bef123231c7980a1fed97aa1577dd1036c5bf155aae5abb4114d7501c1282d6e02dabf0591571d2ee9381f368d8d507d7d0cdddf39560a076ec9a29b9eca7b6c1e7561a6d2e1720d0c5ea1cf8a1247a0deaf8290c491314a1877346b79fc7d406614381bcdfb9d213ce4bebd8fdb67f0a8c395fa94d1b846b43718f8d72704f329fd3fc3c29aa1ab6575ea74604e1962a833274f114255f1f330767763a24d2bd4f474f8fc28a4ae0f930f11c18cfb3dbc1baa0b98edea8172bb0492b818491670c027c724db122f0794c1aed0447a1242569514d62c97e2aa254581969793ccd15cf355331295502a084962a325eb873a7aeb183aeff52728d6f5c5f1460cb063f330c8ee400be583b273838cf689a2a35c4ba1e5b801d12d5bf0b1b8096eafac28046044913cfd8b3a555d7503489a2c9a4befc423b8c8891750d0d9ad97a6128ad0634ba584f8b9ab7469168ddfac1440d4a7429c12ed527986e19d8505f8a1229a1d9c38b93ae5eba2f5d4c8ca0d9ebf9d45dc711ac00b72732d963f035118a4b763fa6b5d71ebd613179a4b38b3ea912e1d034c10e318498719eaa7a856b788b3e3f0cd54dabb1e336aaa1d33877594511b01bbde2e0e3cddbe77adc8d0ea37534c48c978aa3b0aca7ad7bac0802969fdef492d2993e52c6b2781c17f7e416df242225783cc138dc2405723b7a0c311eef271c09178754f65d204e6dc0d8acbd84a8e79a80217704fb4d20804d375847fffaf5d6631800c7aa89c44f37a00a99ea7717bc168013c14b2176aa4136a4e66a1149d572f62fa0139ad9e669076c2740ae21d79c4418b0f001f8ea86f8b5c9081053e8eaea2109c87003391085632aac26ab0262d22eec16fb7e842e6b99bf1aa20a3ffa812eac83c4c3b647094403c889a40170a3f2d969580533a46f6d3be65d51557276fed2bb9816b389ea23941c2d7f7dd3b5136ff9bb870edb0b4cf5ef9f1ab239558bfff367c5fb13b6be4cd3423e678a8c749624042050fc1acc81adc81bc87baaaddc0655c6048475550a1666c9dc367cef64490803a9f14a32789f4f7b9c760fe6c264316b1ea9c7abe76f41912c1b78ae30f222fdb4a70a16371c55c478f457793958fa764343f989c56950140cedd5ba52a9f922b13dd00f65c35e748ca9f2d45d6b2ee586f7a0004660e2637047800b8802609f3298e64b93340fb824166d3bcbedc32d9e39d5f9540acddc63f8e913a8efa12cbaf73d799fc0ecc58ea0086c129bf1d0fcdc40b253e583c8d331ad91a3e76e22c986148af00f781da6e43978f69730e7c231d6bedf62e89ba02a9e4fffe3cb10e49b20ae90b7e1e522fa86e37ae11884b74f63d4837441bce87395e3a24e33d716463d0a7b427e31bc034d8c9c1dd745d6a83476293d75e0e0c852da4072011375a5145bb086fc611641ddd09c6171bad507b4b4b03a4325cf33c422d4634b7b54f68954d7977bfa7063b3ca986f2d0f3cd25ceb475667337a040ef0f28e368af152a4bb8e90f81ffd6263c38a4598cb0b507aaab4303af315d9370518471f2a580d6b8b825354eac3e987dd8a839a0665b2f9e0af2cce99b48c6eeb7e5c8c12e8d63216ee1a71927d6bc0894a376540ad6af332edd2d42873fee43474fcf25463f0e58035a9d5906ebc8716fbf0f654cb612d6b14c1a57a9205e2f36a69388346d436a210a085a539dd86c7cf7df01ac60fc671d2f42c1939b8478e061cf4e10389cbd6b69b0bf1923a99258588350768fa3750dfa275bea744d4a0ab838ad20c9ccaee28703532a4ddd1a1da1dc5b5dd21283e04c3b92a29ad1e13c6e11cae4a74dfd092031122105515f221d0ff9b09683adbead322fb952b998b1a31859b952b01588704482c00cf0aec196823384502207b4cad0e16b78202b06fc2a3e07c5ac003a7ba4833ad93e4476e1543330aac44a3c2b915698820744b13ecdc72285adb5e50ae0dcdb9ed5d7fa6ff5e9f56a103276249c5d1df3da05e3ca4990340424d9f02c8863dc0b6c51bd6fdf1288bb5f3ab5f010456f53921d917339dbf9a75436878b024780ee1d7f6029d608c8237064e0421906f6f0dae2cbcee954d87b90ebd2bcbcceb5a158274e9f198310351b346325f5754d938366e10d7c529fdc31bac94463a67837f4848bea691dcbae1741d24512a63694e5966197549c560ca4c8047e450a3d0c18c997ef8f1b24e6fc413a818f7ee503ab22f29bf2c538d3e119b06f387a01e1b1a8dd7f49677eaa6021109f8a819a82c18f18035c71bd11e448768710352f8e71a40694e511f595017248ea9798b710e2440b50a545701f1538114d440a776d82087bd8be7bb4b0059e6d80a26aea0f87a021304a660810565575e42d418bf4a33c96c05b0c26b597755b5e4615048a3c7aaf55cc0ea06a03b0dfdda89eeea019101e1e18ed5221b82409961afb3caacb0afd3fd8abe4fdb79118340e2a49ec6d7c6299a5cd2a90042223962f946c17502a3186034211753ac5a6b3c591f0375e4c6289ca0475f03ea2137064102beba97e013068470d044b9624a7dfc99268f963dc16084cc1dba29fedff270c983e3ea61d56b08d4bb80818e5110fe63aa9e9e4508828895ef82e88fbbc2be06abf8a6bd644664a8c730002ce357215cb67cb6a52aca5b65b2e8bd85dfab2c2e726f33863de87e7f59b7105baabd8a1c24f5154cad55fd4d743a653c3c2cd23d5a606f71e255bc95f6b7877d86e80435b5b0160c84fa05921b63e6f429e51dde17f08438496e4b6e3b558df03cb3abc880c09b26ffd0f5fb25f8b6ea63e74d43a64cf47ce5f6ff88c86561a413281dfa49b6f5786efdc4137adff645009f0e8ce1115a62484cee75e1381ae9bd4ccaadc4af5ee3283ae08a2d77d0d41c20cbf54c39ed56126dbafe25ac12f1f58842c2e15fc8f95269ccb8d0781a39fd0904624884f2535ccfa40e9c1b68ad82db0ce89d803280a0d14e6ab53216651da97ead19ca638b5b8ea89eefeaff2410fcb5766cc6a15aeea610f0695874e4078437012908cc293435f1a083ae00a94ed8a9b52a4219e45cbdfde0c33a4aa473ab85bb2b62bb84c1ccebf1acce3cce52f7ed67278a20bc75a0b0bb592d0c34dcb1f9d3c993dc5fcd8764df8423c7ba2059aae7283af23096e9cc04fbca78554eee731a095719aa74dfcf2be1a370a1d13c43ca8b47e721a3be5804f9fb6d7d114dc7824fa716c8bbf0976409590f0895b46d1e8e7af0884cfbf796b96faf3fb346bf02e10de3d53b5115d996b4d5ed2f54319ae4fa5e74ae174df351bed79396faa8f4f4cbad9f58aed7cb778298de1cda7aeb50d6477e935f00b13e20b2336dffe841f6021b531d3524e41c14d783f48c2df0b14922e6d4fffd354604493a821394d00e86f9d80587508f688c562d7f58bc3f0e7a94605c49568a4f4219ffdaebe979f326fde43236eb2a83629e0be94850288f61ea450f5127a2bef8800b993f265c47d7de9ae0cad1c6afa0415987235b4be9b5d76a461d41ea9100e2ee7227e0363db7fce78d4a3a0071700e9063178e0d5c1eefde7c360da54c8d4d6f000d090c4839d09378b390f6000cf74e75750ddf0ea731c643ebdbba43335cf3098738be733f60aa318916d3bce45bc2d376f774e038dde24910887e20b6fb1b74b3cde9d6ab136560922712d80c0f98fef2cfc5839aa8a3d1e84f88a7dc2ebda35c578147d387a92d086eeb9142905b5040460591260c6b857e8adf496811a3e7e536f88151b35ef6821c0023cc19abca67d9d8d1544533aaaa3a5c5033002773ca2c3193bb3d6e973b2aff7843d0339f07fa54adbd778bc20f36d34578398397a3e2363cf8271b16dd2ca7eb3171ee86b25f2dd6fcc5abcec0ec9717d701f65fe745c0bb4f64b1030469ac6f64c81302705667912b2d9c437e4e069fe75f98015818f8b9c9c6f4812cdd46576a57b5b57ac0fb2698f0a7a77c311f1a71ac298fa2ad0454da1f0a249431e78e72944a105304b4fe9e335a55ff3400b7e23805fb0e44d1b1fe91cebc046ebe5643d57b925ebd85b0c7c0eca15f22e670417e1ff3dcc7241b118a090d4c241f1f50120e34edaca5e3b617f683725ffe13fe478ef4d9ad73ae6db21cf118c18fe8414f0c1f379828e8b78a645117c6899c84d7bd982a358c21211c62f7282d5caed3f4872428d82ffa3529526fc89748335a2e1bb7a76d3c3180259acd7b1863d6a7e2770860b240e546b3a7480c283ba78976ffeab77d05e14650cb220f6c4e956d7c6d162bdf5526c4a28b87bd5fe7e1b10d85142f6575e62ba22d34b1486466e9f3829393e558f627a8fa6c60955e2e503273cc813fae0ea396aedbf951d44664267ec36598cb01e508c265df7654acf37f863485937a2d7962a7af4e3513c969136172af3fa59e9810e9ee1ac1eee943d401b56c2b8aba14bbafa46365cc7fa0b35b89c35b98f0b397a9317c8630297323393c5f93e4286e3a3f4b5b3a3c9e63a47de356dc1d10460ea73e206019101b318a8ae303a8f77fb5a9a5c165be836a950521c268a00e17f619afdf686a2823dfb8db09556bf1ac6ff6094ae316ce38524dd163a2ec9aeb6e7ca63c8539f8845efbfda0bda8985db14e3f478b52b4db7151fe97f73a556cddb8ee20fab8a8a8a3292a69e2ad42d74eae146b6a152de8bc14c6e0e90766a0a74e896439fd1ee98d9f6e64609df8fe10b0c544c2b6ae5e995af0e787a96ab4cbc83fe0012dff622fb3647a804a196242e7d50b208241c9b6853896d84815164e53b5cc327054ec89c5805503575f6d47e715775e6db378c985c74ed200d56afe5f56441f3726e48ec32e83a2849170c067606534c26698b6262d61ce11bbc86444c8f1a823373d41bf1dcf7c8391e5bbe85d73437f94a30174438848c571b6c220cb64173dbd402111f7957b88ca0a9488ecb503ae5b3e7226e11cd2529e4fb7d8b5010649e7c40e316f6428024090749cf628aa9251a9cb5a54f70475caa2fc4c1bb19eca3c0df2f2c99069d0c5874a1457d84c1ed6d38a0a821fb624313492fed34b4abac349b2b76385f4ae90da920474dacd5c84af314be5ad22b798acd9a89517fffd1b1209b4523235cd2488142ad55d5a91aee0eae5633d8420dc04a96907377de3039416441d8d007d23986455a3b1609974d9e19ab15b6c37583416831b8f7cbb5c45ec289c4a7835d0c166b22a8af823bd1706e8245196a2a7d347be0407844c58d5332886dee8bb081e2a3d13af95f677630fdd85f1befbf0ecc628fec511e36839695ecf91e46f9615a60f51aa89f968c1a4fdf5eb7483cc6dd737f0d01d4535d377ee864264351eae7eaba9a9734d1d089f108a700d3053aab0c69f3072aeb0ccf3c5ebfe6630c9214c2d6d0f866b0267ea5e178a166657701ec9658871c64d50684fd9af0f48d3f49416e3a5023b9b1ad8028ea2ef6f029f243b0540ec96285c976f90886e9c1d84926d4a3c3085921ebda8fae1704a94705bef3d67ee00bf8754b6a20c87000737d7e9b8505e9da5d34b0a9f41798c408e656fa01fb4d4c2709a3dea9b5c6cb71d648ee64e4fbbdf21728d78bd5f6e2698ade5fb1a889588394372792a238b789a39f50fc04a5bd6f3f8f0d974d94a4c7b2e988839032b9c4e97c99471415c3bcc02aad3b5baf0e7a34135f5be04fe4f739fcb0e2f9f261e28e745088526f554b9342d15f793deae04f41476b19e28e3faae5a81b6418a79a4400bfe3f5b1452e9b491575209586b71ec9430d588bfad0cf46666cecfb69d8b5c749ea5e61bdc3abe2e54f8008610428cb5f086f49b0bfabd9700df6afc5b951afbe2ea654378be33555acff6233d99023f0addf7117589a356649c91181fcf868ab58f94da7f5b5e5ae4fa32164e4c5faaf4b4d0539ec9c1a5190f08357587e040ac7081b018b066866e07cdb7a3ffa84aacc28031ba3026f645f0c162bed82f28a5e95e46106c5c308300d337dc30214684bfc1168d89e4488b89daffff624dc0d6d33d7f68f63f9dd110bd91fb102b3b40c14d7fc6527f358d82af428b119cfad20600fa61aaf6ec75b5f9969b79a6747b84805f949fb3171135c93ed5f10e3018dee74c0176c74464ca1c0c42a19290940b3dce0d02e4e7f8fcf29b61903bf0ea867d7890f7cd214503501605d451a89a45221ddaa5c239f762438d7973f50a9921a685bc485f0a94afa7cfaec8419b78ac695032ec99d36c414cc9cdaa1063763b79165faa0a160bfce8b0c0671111837140f4782f0710baeba1c7be2a7a860a8265a0a349e22b6fde11e7c0010180f263d7580119e7994e9dbb710853ed40b8478303fd37e7a8ebed6049b04ce7029ade2019e7428b80d9c5780f1d3de72510ffbc8c991f940b9f1c489a7c1f3b9642c431d25611d08d91a9ad086573aba1df7af3dcfa1dfb82831992c43b9bc111988a85ce0ac7b3d9f2ea9df6cb7bdebc6dcbcee3c19dee3abe1a13ba2be7af690b898d20aacab840f003ee5f2b491d3853d983925e54cd9fe1c06f15be1c56c9f954a6fc058a53bd27b6fd185afd06e91d91bc8271290daf34d69ccca57cca15e3117b8c94f9b74b31e80b7149c8350b5a77413d0cd26e580712af40a276788c1f37ce3e7e2796621b7d450ec48836dad0d2f7c857332598a22431c868048c8b75e621b8a418c0fa2aadaa42223cc7c66052f9cbea1964e297af59b550a79b7d7f4a22744aa351147b780a6596f2f8cb5d3137f3585227ad8a144ed9c9c71a4889356e1b5dfbe46a9f0eaabeb94c6ac46ae6e8ff421c838a51510117cd68fc0cec691fca7216cc06cdc259d3e2c07a2000308204c4d590ab208136fb46e0da98c8a12f94ab2111771247e390dc74c91c05960afd03bd9fb41dc5876abf3e75cf3e5ddb70c82604f3b068f0818aed2cd7220415e3f7b15ad6d67e95c0073d1ee4040da472e1eac4a61a00394deead1ba8f47aab10cccea2b5d67a70ded1d76ddb6d0be1703f1da34356835ca9fef78e7ee65f92a052f95c5337d62163a47e53bb303c14d96d558143695fb791b226a6c72732d3095fe2ea3f48eae55681bc94f524cf07cc1cabe472ec016d9b3f35102d45298e1156885fecc9804c477cef03b59faf5f386e93b4c93ffe454874521ff50820e8669f286842212f7a494c615bccbe872863bc7c73ada7ce363efb0d6cdbcfc493fb3764fadbfd6c7e2c8978bb62982d53ee5618b045cbf934d50d4f4268e77033aa0872c5aba48da82362b975763a5c3bebbacffcf095f5cd127531bd9badfea31b195bffa9e6edf83846c58a03af2b2dac296203f4d5dce01679f05d3d768709d308735bb2806ea297f6817b304c42fa08e633c4dae9be9daf7b9764fa7e72f2f7a2e5fc2417d756df8853673064f5a911947bbd52e2ef35ed3821532516e97f9191b41546a24b09206e7fb404694b0b50d6458a7f0e55a907b3b36533241612a436832560f258da72e8bd09682da2da4d8e177fdebb4c6707ecd31fb947300310a00cf9e7bd582e430c675500611f0db90c21d4599c845275853502fe710b2b11fa585de6162acb22c1c5432915776f9b13535cd1c0d674b403ad82685256607cf0f15999d0c35f0acca9bbfe4f3133f92063a73fe1691f044b778f69d5b1babed7f154e30fa17d84c4984acb0d723e2e959efee81174f970895edc9902a68d3a721770c14582a0cb6e03a45ddd62511e8da389bca74f630b1eba9645b6cfa0a018b41718059e4970a208f6dfb6c9d26061fdfabb3572d553583131d150e78b7e9864cda3247a5eaa677ab3e8496f3859830391ed7fe6eba169d7d7651c6606ab96dd7c43c4ae02fdebb116309bb8380e6f625bac0867bdb27b2e9553cdabb1cff932c3e3beb0bf50e2029e9e13779b0bc9279e19e4549a78981b1c53d83b01d9cc2f69fb485dfe81c9e803d5bf9ba2af1a16a76496e7ece280322f23e9d24c85d523a09abe2585c3af71ce4cdfe1fc8d7586edb2fa09dd65d791a861b57c243db71fdc7765c047472abdba467b0cedb43bc12db716deb8ebada2e5241dc604ef385b314e75bf6e6b00fe249fa3025aeb27f613b697005baa733eaf3bae1d0e883ab5345201a4a7fb39d61e29bb05a25a542ab1b496cea004e13b01a2583295680e47a12cdbafee4dd14d7b2d2700cea0877201f7f1af56195744e591db3a9a92a46282b60dd6c63d28b8c0335c24b5357f2958248c08dd8208ca90decbb5110ddddababd7e6bb6706e4788448166ff526986856c010d1fa5ded3066db77621c65ed524adc0ed220b196d10cc154537ecd4fc29bb2f1aaaabb3388c7590711b11002f2b1a7656c5505d0e630c5a1282434774c1536395dd85769fdcdba95c7013f03de5ff492807a509d1b55e319ef491537c0dcf663f9087f185c48b210d4e9d71eef6f1ce1721d60895d1129569dc98b8cae696e0472e622babdc0ae56609975be1c193c963724ba6d30dd9e95b849d5b75beb60bc392b1d0d2d9a8d8abf044c631b19d1610af157a57a69ea213d55b75122e48cc01aa3dd306b86bc5f2e94d7554b4a85c75de1bcfe5dc2088d5c7c3b3063501eafbebd08a6374e72546d6310da8a9ce7bb9f117125b4e19844cbe5b59d94145c278a20c624fd8c075dd1c2d61ff155776a8c4472094993a113eb9ccdd9a8406ac8764671b4090de4cf2304682497666b6f5bc737f868ede6cef0b8ac7ce92367e9e786a4f17e539b7b04f0a7bc9c4dcc5f36d80754851a4263ad141ee1fdd7255cba9242bf3e115f0944a7d44b49b3a911e9c4221cf637dc26267353d5cfdb0a63fe7be6bb0903eccaa0aa3ff71839cb511c7dc3ebd914635670a1aa98091da45edf3041224d4a12c58e7e8d6ab6d2a352cf5751883a3f473515f52d836499d10da69df65cb1e84d42daa21b9c869827d58a0f5569c5d91f57beb05901dd420f068cd5467e137d7093141e643d3b169e657b96e7147e83033acc628bbaff808a7dd27cb72e7e02c6cda439cfaa30a213483f41b311f14ee9ba43d762de3460fdd13b1a21eae8e899411bde23274d122cb26feb3217a634ed5d209c61b7a904381e3f28e438932c2dd1922b7519bb44c6d682d13de96a98ca699f3b500df5f5a0d5e8c85a754a448febf163b7ed2cb7ee2c1a6bd846d9b36438aff334d456c5ada4b4c36422cd4d356aae3e7d87a0b509ec3b141bbcb4ebc36e427c7452fac0fbf9b76ea725a4872123aa305bf04b69580ad59c3f252228bcc78d6961481625ed2187bec51a4e18682faf7a97019829f9a2064b392b5c34cb0e142b42949fa2b4e9b108b8adff50e995e202aab3fc16d0eca28ee515529f47599ee5189efb86958a679e198423e577ba58050e4308f98bb4fdbb95c81b536f235755adb495d75d11b17e2e838f2b94516ec1060f1acb507bed965c8549657d127b16974bebe94d67a57a662daf3e1849b51813032f532afbc05289d78fd91cab41deb2f19826651cd64a5c103febfcb2562354f5be37e945e6e55f00ec30f2b525b260c3596653b3d700069bca55bec99367c2eca8de07bc48e240e198748f27e490e13f4041b118bbba3fd5d211afe3975d0672dad75b94458ad1d4c27c541da83fa4af14e07862820e513bcc9e3b5a7776953408a4f7b7ebd639dac2a8ffc5027ef4765a94e34a600625063ddad708c173fbc115f038d24667f919bd39cd64c81383776e23e6c4e8ecd77db29f7dbe6b0cd0988e623c4c551a3379d455fd24fc2e5405d4221d9b3ecbb722158ae8b97ed358cbee622e6d7dd9072a116d437bd974ba00332c22c8442d4bd8b5c6d8548a4742e0d545578eb642cf4292e0533c999cdf2f3f2ea7906039506ff7e214951f2a13122f936dc4b4999979a311e85c60e3c1ae3e6c808455e02672171b5506c535aa23aa198fe75a9227cd31e1e9d28c4336522333fdb6066d67ec448b05966c8fa3bde435a5f0076b09b2771eaec73c02505f9ed1e8ae0b2708d4b8dcdd8d46b04f2d61c5820644c3adca7008e19f2d241ac9320b33a39fd3668052f969c38f3cc480ab2a76e7f915ebbb3918b3bf29650067d550c802e7e288ef3d6e5155a05eb158bad319daa95573408075617023214127edef01f3888469bad81cd2a63fbdf59376e0874c504ca79223ab1e2c7342989d6080feb5494662b8441781acdf366ce1034807f7a256910536047552428a6d647b106311b0bc25d4463ee202101bfa2c60ee8cfe219714c115413020581f9ea8cf39e0a66891222a178a1214b88748c01bb6bd115fbfaddbe58acc60346c94df6f8e42c9cd775468524c4a79ceeab80323f4305282426a19e60865b3b7c864b3e23d19a022fd444150095d3d6ccb102a15539977d9e14a10bed2ef393c734c4724c51ad9cc688c082fb9f6bc4faaf00a5359bd8e390ce68cd4969e58dbbcbf0898da896ed85627cbed4698bf1e9fd084f5d5e164b2a33972e9bf2282f908ce291809de7b81bef360d33fbab6e60610402f20b90fcc2e248e7a169d94b6f54648318bc5a83193cc0b212dd5b740423a723dde5cc98d640058dc0303771e45e302230fe1228d84404d391e9ac01bf7ca924f525516c24195f8b50dca33d25d93fbbc152a539c4597383dd4afddea862341b9c12202e3429c45fa8b1f71baf0daac735d443e6c5c465432c90ac52a507d92a102605cde6d114d5d48d1b6750432f64df3729457a9d11c6fdb685d2b0d0660a6de2c5ee79e223aa2f3fbe08d9e4c7481ff50943e740ef00bdbba3d33dbb133544082e4843bc3ccd2735a751b027ed09d68df3dabdeb3e53e07d6cc6f357fcf6343aa0ab42458752d615c8be990c1fdf5d905c65268541cb402ffac0a0109152ee4914052a24319d3a5e411bd83ca5b63ce7df63693fde13e1e9ded4d4fb5d1d1cd7be47de9b00ec66c47b12bdfaae74bcb371037ccebe5fed50ff4cbd8abb8d3b12816252712794b8c936ee3899bf634529d3c85e6fd4dee2b61ddddf4732007bf66d8e99388ccaeba535489edfced10df4c664afa45f68af14f07d7531d1b035454e26c44d8358aea5a8a94142bfc8deec2f2f0dd38a520be6313dde5dc8f2746da2055ee25794684deb1c709bfe23c85b2b6795233115bce88409d08d2720026c1f74df0b8d4f2b67e5559d0b2dbfe4add101255ae70a3880357687723e8ec968d1316fe8adb3bd53073844cb5dbca01d7f2e7057ea560a8882be7a903b9bce952400e38c95b43bd05d3edc38dd4240adf07cf662f936e0111953d228ef7abe884dce19d6dbdca93acb686bd38f3c14a7515ae51eace8dd5f87b70930bbe3f9885e8b0b22fb58366549929db0ef224a42592e73ff4a6c0b8fc2462bbb8b26d9e79515f5a7912365fed14460af02143576c3f20436d111f94cd4490c38a1597e71ea904312cc5cdd8b00bf9355607d176645005d2276bd31325dc759e75e7557139a38efc16fa343f8c1971fc8ed11a406e57f5ea445b70ae30aaf7409bfaead8fe841f8cf32bebbe857f4134f1a4d708bf7dae09ffb277db96cc4ac0b91cf73576ac277da44ded7ec03ae3e961fd4042cb5e8e5e0c269c6452671999979fea64af07f0df94b2c25e759e2e041999a35b451fa96ea9d419e96d51a99e56bddef5feac3714ba875d294cd59f13437613ff082a5fa01f9e41a60dc4a30bf74023967729b62d28a779174930ad0329db9a38cace7d49a7b6111604d9e6690f4f0286239742fef1ea1142de963ea430d4a1756d40120f2340bf2b9653bcbc0d9aa4cbd23f129b02c83a4024c3745ccb2090d3a25ab09640b931612b585df0cd7ee700031b65e3aa64143e70cd52a9d13a0aed541e4bb6b57bd72f0806204870b8d03a6572e48e1f5696e7cfbb3df7dd63e20b925e0b2fc33c477bc8ca798c18ae4cd2051bee08171067429cfba8d530fcb33bfc8c0ef754fac436197d1ebc6026b15e3240f3b4b8887826c0ca66d13b0f4d81b4c6359ddf5a3ab2c6019acf26b4326c942dbadb68cae526668b89aa2fe585fd888454aa5fe2f0a12e6a061c39ddd1d7862c3ced495add09b23c107866cd8769939ff946b2e5a9ddb94cbee7142195e0c0a6aeade495ba06de65c5a03d445e0f744151e2e8eb2f7380fdf955984e37ee7ef24a6d918bc7ff970adf6aa091a1a673300f00b4233221f4691dd33d8039d591096453846cd008ddde6138e27585f588adbc44d7456f6613da83a8425a42c1d6a22d9e09946264b839b62a4a33bff55b19701f462aa831a443e0551e674fa401790bdce07f33d92e2610d3b21c7a27f02765f4d25d706356034c138cb5ae2a4bcf43bca0cdd0594689512fcbfd0313be47275bea20f2442c5823728db6ed37cba2284765bc00175e3dcc09ace8ab8ba957a299b48651dee52e07f4e3b779a26c952cefc329f638e48c5221f0e256e0412aca0e3e8ffa26242b8125ae1a7f1e99e79f4a5e71edf8c4a39a860bedb2076ce2ca0c355504a66066903a1fe3229a3f1406f993768bf3dc170d2c50b2c357bf9b69c6a089a1179ae054b3d4169224df9a1f90410e05ad6b18a68f966445e52f361de431782a909e8453d79c41c5506d41afbce53eaacc57af55f4d71db2ea2a0b9260af5802925e52b364dfa60c055c4a2b4f457f33d03a923d5dba1037cc3d2fce765471183971dc764b7999f43ff47b1bba9881b520c91c9c455b379c2f88e9896ab8ee6ba07aa8668263d73d20ad08fe61e6d25946162c3e8a4de9c096b618e91463f4586fed87f0183222b07434f86819bb6efd90cb3ee19e5b85062638f796139b2a8572b598b026e0132104947a253125fe8d56c5c342717cce4fdadf2c28034fb2715aa3ead5f043abf5f7a5538c50a08dc0310844fe61488cb3768ae84f2dcb25e1ab2102ad9b97cf80a6ce4ec4ee6fcf5c2b9f1117024d2a51e44364842c3aba309fc317e6cd899453eaf6036fcb8d52a7006209a81fdd103ccb530e8f601990a57d34719ea49e6bb0623456d004b8cb65ebd9018a002ae8f6c2a5f1242cfa3bf5fb2f40976cbc23dfe5d72e38650b2235256c8b41bec418a650e268ba85d2506b8d8b02b20d7a6b96c4e64d6890f6f54413b6cedc90ace3392bc9a0c239068b72fe8bd43d8966198465c0cd44a50c11a1abe1bcc022b3050a3f1261c2b5e9d8dff1b32ebe550e188a54971ce80a31e2a059775f046878b12c127e897320652662224f15cfaa0eeaa21b1f41a51c8b551d381a5a5db9a84b462c6ad52129649d24182a7a49b8e4258acd6b5a8eb4b35664a6ce3ddc8c4e0a1ed0116a657e6595c1952560342ae93823e9472e894325c6ce9bf9b2fd843293f3b1168f8ab84fb9dcdbe3355706016c690a8a4eee2611a4d8c693ba3a637f6a468c938f0a973ef40ec63766cb594801bd0d50be0001bd06657536ed768d5b75c5f8bdd72928ee5cf7bbdef1e6a29cb1a127bf988263a9b59f9e437ec2673c7719e2601d8f5bfd495f711e07e8b9394b6dcc4d4f54284bde98b88057ea1523674eb153786b9ed87873c22c0f29e7cbd9ac73b5b09e7799b63fb161dd9fa84452cf51b6fb66bfb06615162915ef2578d7e065218d62eb5232960bbe76dfae324e1ecc6f47b2945471aa7ed994286d7a471e454f1d32c91f85740135a58a836570ff3af015ba6902d4b4a36e2a3112394cc3e61e55b4b61063a2b41175c3d290654300330faea4153a8e80e15ea4c19c90e65f253647d533c11a44f4c5a9cfa793a67673b4819ade1625ea71de20e689d2e7124da1ea93e5395970ec7bcd9b988f5321850fe6aca923570913b28ded5a855ed0e7e3d990a3b8902167db53fec974e101b410da434d2f41d22da370138545055a2e26357727bc5579c000a87c081d2190181883707209d95c683e633bff77287a8b9014ffca754698a3346c93581a5ddcf1202096a199c0a85d0055536fa1385238a7e844c2bdc19c130fbd45001c5d42fdd1a0b874541e3bceba6de69be3363d870f35aa368e484710f02cd8da644130f2bde7b16678384fc3ef5adcbd0477f18a2e738013b30f1465f2e4e4d6000bab2bb08b0ae8ce2959376a9ca057634823b359501a70e7d526906b4f110e6ef829c12c4f80aabb685f42aa375e740fc2dda7f20a6d8729392e5402cc447e04ad2970becd5889cf7c50893277b9eab4798b00e7aa4489883e39dbc2e8c77dcb993f796304ff402eea52bdcc6a2d0f87c3b89d9d39b1533cfa96932336e2bb2b4cc04aaf80bc65a30d659493afd98d7fc410f1e657a4793aef8277e342675074e40715bfd247a47d59371dc43573f2115df0cacf3a371fe95c0d35bc8076b15e9575c4115e12fce2c39f537baa2b0d7842e5b11531b1b698d1042c8de7befbdf70e080c9b0c380c34f213f31004d7c1a121f978df030f3f7c66bf6dbbb61d74e0b9de52c40e4ee083d337fbd874ececb0818b350c3ae30647d2cc9bd5ea1a71d864c25d1670ddbbe2eb9575641e54311b657f5d67a4997372b37b4b1c9c135cb56238f33d1c08c7958166c79947231ec3a9b882c2c577b3076b02ae7b6148e9661034b2c3861d9f87a56b0f40cf6276f685675ffc4080a484ae35df6ba916422631a57816496bad9512be2bddf9acdade7b09f5bd1d91ae32da4b4c5fab9552decad367999452c6ffd88e3fb0e7e7b3407a24f07dcaf7241676d82270fa3d0929a51a7cd35a58a9acb5d669f18c997d66f17d71f568c0997ebfb6a6eea4f4bdc22bdf9c300842bb67fefe611d4e5fd705d4570f353de30acb37aeb0dcd920b47bec2bf689e5f82cbe589365b750b87b6f76d15fbff3165314642f336120d53d4f755595aff2f7f41366d5f51ea9b2b7393b7c36ff8adf2f2c0f2fe1b57649e7f3aa40265c64efd953bdf7ccebd6877b557d0fbd7ccd9a0ce4697c59e6b7c42eb14b9854f122e7d178284a29a743d3791b030ece34a5746a97d239e79c45707006ac01676ce05c21b20e6620f311de47a885de7befe59a199c0232af56827bb661ecb1f2c199b4da1b57ef10e6f8856697a0a6ee28adb1259839fd673c1af05688f8285eeb05af95da6157c0755a471f1c3a4b7193dacc28562faba1a24d07218419a5c5d66677425ab563ded444b3dfb3d941deba80db8e4842c9d822ae9359657f912822c98937ae68ee525d2afe9d4ad330dcfde82cfb2afbbc37d250abcade033519702a1d78ec30d35fa99475c2752854b4e91e10e22a36c175ffdf748aec457466c2b2bad23d2a1d73e7b302924f7352d4d6dabe525a2b897455d21d109494164521a00c314fed469865efdd3b93c415a552662de21b73fc8bf9515cc6c23a0340a79c31577125ad10d1ea303384cf4ccf57b8c39bcc9a194fcad24cd6834d0234515c26eda1b5d64a29df636683f7bc7ce3e744cd39e7ccb21d6c130e7e02d1a632a928d9fcad99529a0226848031de7b2fd0b4425cb1281e218a48909268c5a2586485a0c24ce99d86bad1dea5b6d7d2865fa4b19acf0c16e50f19cc2586e92eea9dd6956b7987eadac9d46ea6575d6770ef6abad6c06ae4cd4ad2c8d7d4e00b85bbdf116db6c7dbd0ddd40b4cee9e95178cc23c2ed8470bd6618747439ec337e24ade8473e24abe8437ac61edc2ae7d527c705e2035925de2ec1797eef2ed46a8cb9f2e2f2ff3866dd48834f142e17018b5fc74a2d873e48d8b7d47db29367510dadd0e5de2a691cb0c0d5539ce25673912ab5c9c557858bec95e6d2e14cefe7aa75d289cbcc9e22fc7a55256d64c9c96d78c41c8de4c07df93adbe9f370c7b04677fefe7c3d258fabc564308a51c413ac28b222445488a901421b98a905cd715898e146d9bed7154148f6ebc41dd7e461bedf63677409089ae3577401209d111222445910849d14574840849513c42725d441712ecf4461b0e2eddd5228dfd09c36cd4728a8d5a8ec25ca4a1d7b0c472c5f32d24249420211106121256201d8105d20caaa045cc2552494c722488b8b28f44d1c61e298a2b9bbd8b45da69968132ea3577a56f7ff5a77b7a3deabe723fe117694c77c12fd2702dd9943ba0d9d7a99d6db56f36e667738f368166679f401248367c427b3e2281345cb927b334db19445b2450868ac9e9936afb6ae38f9e3cf43cec423793eaee0706523d734fcd51b968638fb56033d1a6e6b85e8f61788bb98a4ff84ac8152129425284a408491192523c29913d2b0417a58c55082e6a93764048d92b4d944685ba392fbea7380716b5f5520d85ea22dca5a5909c447afe6d393c2e58d6c8ca4aa4e5e9b479a866ecd2b5dea5333864838010ab3e5ec91ed6dd137a8fc7e3a26bc53c6428c20db2f4fb7cd75289f50f8ab07e3332297ba5ec95baad610989176f8c6037e2441ebbf03bda6cc05e95a11ef2b12203844ad0b747cdbbd2809cbe971b8f686afb0cd25247a428eec8e9fbb8e9c040ff10d1d1ba181820f482be5b0ea125fdb3038caf54ea882ce97a7b35ece64bc3f063e96091481b55d1f5f8327717c88babda63fec8ee91a7cf40aa81525df3213cfcc42a1edd063abe464a85ccfec0c519f4439281928e30e88704032df4aa1f120c8eb03f709209fd905cd06a98d40f4909183da31f9212253f3a3c553ce94e4717863ad09d0f467802e482ee8086867cb2e80ea868e8a73b20264540ba03da22a9df7f7438d05d064c70d4ef3f3ab02b2ee842bfebc07c50a57f5e962c55fa5d07a7c8957e2f4bbf37e422fba1b7d807d8f6b0271e1e8905617404021f0dfab2743c86c5fcde0c0aed8fcc70578b30bf2939d2dbb16714c3a60ef6edf7da21d68ee1ed71e3711dfe6578ec1aee6207b90d5bc8c2f884cc562de288b9130a72c263edb3a892c0f57a3f33cf35270e203cf53707d5e3f0582949239f13a7b453ae7ae08e13ebe48e1f4026abac31c652bc8c528a2c9394527ae1202754d2a0075021d84352aa83598aa34e064df415870f3880a086ecb21b0efaabd2e9845ed0f175061d339077bd50f6b24b1be2249e08f60b737251fb01ee21ada08b96391e80fd82c0635dd949dc7000410d2c2c675b8f9d7b10d4c0ba7eb1e2c60373229fc54de75431d7f142f03e59ea20d0efd1d0e273e20d3a7437819687923deb010e831fc28b8e3cdb104b3a0058ac5cd8af5c0309709f13c3e0b05fd8eb151c96553a7168a8e5890c0df5c583fecadd4b0799ed733d3b6da0cea6a49d85125e4edc4a275396994ea793a9e519d7722a95b892a92405945d9d90bd992c76d1b5934c49a2a4931c919ac65d155f787681b3af97af8132dccf88abfb558d48737fffe2fdddc2aa46b582a3bfef6ab41260fdcca22594b2b14b55675815afd20e555a3622aa81b5fd212549423fa42458f485e58e485fcf6f86ebf7f79cbc81acd2ef0f69e8b0abb26b604a6ba0075dc2aaec9015591c5665d901973b1090c5fd19d44a3ec0363a1dc39d067cd0d8ecb7e1d8313d27968a4016975558fe31656923a21aaced454c5d60010159db8d5c532e62b364c982c3cb8265a8c8cb826593fde0a58087db0ddc0db2ed6fee7947345223a8004be77e482ab04205ad8ea11f920a8a5041108dfb21093185ce4c6ff64c4ffc94e1116d728f7c3c47d41377553ee69ef8f9b9dd106573d146e69efad714294908ba53328f1db7792edacc6d076c7948436c79434d10603495423f245d74fe813d61ee8a8e347c7cc72a1e540a0d2f714f7ce78350c3cf7e7135347190979e71853b4aa1210ef2d2b863eee23b1d78f91cf032773002991d1fb38a07cc4059bf03655d02230d14816495ce7c42837e3ff56b234890a4e5af7ad4f4d72311d4aeec70eb217764806b08c4b5bd6abae6aa178c5b8febd7150fafeb089696df7e1e76285f2cfdc02c70fbfdf69844e1a545b8915e7aaa699aec4113b518c7106f9b8a045154df48c0a4e5e5a6bd4dd3ae65cf30de345cd2b2ec0518ae637a0106d3f69ca82add4df57b95ea7279215af1824aa552a9f00b5c7de1307fe185d367a81854128b2a866c73d166b4b97a64fc7a4d77322ee3d7e5af960b757ce1f685ed31aa2676c3916932cf892f00d1c9607866bace76ed190cf99972a76d4a54aae744ec07b6ea59ec7aade949f5b9f5a0695556f1486d5b0e780dc6c01d6c197f5b0f9ade8e63c448c5609a24aee861b00423ae4eb80bea162c4125dc0535a449a20d94589a1e4a30a215bd5df57047b54db26dfbc6f343553f1c742106249042022a47ac1ca172444aa6696fd3b463edbf7e3d075dd3b626dbf69ca869d77de9f7a5d728a24de9f459a954ba4adaf59cc85d1888ce75edd234ecdaafeb7a107754c96bae2f13fe815aa9b41d7ecbb0495cd1ff58c89f8f0f877e8c3baa04620d5ec74eea35086b06824dd330d728b61baebe72a781be9ebdb880a167b71e0f2182ff0fb19e8d0071d673e4f163bdbea34ab42c77324b962c551ea34a9adeb0eafb9c88b594af517454494c8a2b4ae193a67f4221134db9bfc917f0d3b9f1e0ff72e3f983f29ff1eb912ad214c95e2d02136c494f193649cbcbf8a6ea9a7e9bea393116a0df26e308aebea6bbd8dce1d643c6bbd85ae40eb1963ba198a362a18658f50d77321ebf5d5a893631c410430cefa2941cab8ee16fe389e199d46288e139d1648201c300c36180e171c301c3b6655936022299c3ec85928c7e38e082179dc9d06270d751f3136a88bba00e7ebbf64d7ad105f5bc56e91119d364cabe75a6d2e0062fb335803680c2bc83be14471d1383613e711ceab7edc0e494a48126219798d22283cf1d949fd126defce62c1bde0c72ca944721ba1fd294a1fec4a83090788005c6c7df292590d84f4e09043650c4112876e45143c753498411a6119e4d0c3f2f4fdf93e59b4148c248b4032f6e0a3a7e07431d8f9a53ab4770f4dc9c6fd5d157baf1903c3a407b760fbd94a73cb0d2f442ad33e9a437ac36cb052e76b75ac5f89720b1ed658e99b91bc49c68b5c07528d8a9791bfec64320d8134a81518a122922905284889291f2830b0bef08490a137dfb2149691293ae3067c2989973c61ca8e04e2acc9c83125c9730b440a7d441113de714b07e485550014a19a90e82d0810776ecf8d8a10a29b43c0f3f2615e6cc8c1a36aa8042cbe7dca0421556320e15b8ae06cdea1d21552184b6fd90aae841772be9832b57eec5c18203274f15495a45f42024095574ed7933593af08093b363e706b3e57de8c0034c42154f48a42aa498a92f383804a5af7e485530e9eed2276c142e0e169c2bd98b3638e26fc7f70c7e6218f1edb75dfaa88ad1e6fa0d763aa326de6837db4de9f43be20d57c21bd6f0bb34f1662eaeaa6c05d1280d8949d37332f2497104d98baf5bcd558d8c5db4567360c346b4b971f58e8e9c102549c78e4dd4af1f15585a867e4854eca0a91c49e9f85a371ef13576ed6057999f23674b492fed1fcdb0c6ff98babeb35358d1416657d9f116cbcee0466f71ec9adfa3015e79d2705e12e1ddfaae74ad38f539f53ba24dd6f53b51743d4fb4b9bade47155daf43bc89c1aae7a1d66a82ae50b4a06b9d41d75ae99411a61fd2145374b74ae24206292663ada852a678d2f7de9a712e7ce51af7fe462ea68ae87bff3783cd5cd448e24afd90a6106246a49932e64c2182a6fd90a6f86166d962ce9930662096380499f4005f952b54dedb49d5c89b0380200649ac776845260979531022932313e3155bd82b20931e8440a22b74844a62db8c93b3030a19ac51c346b4b95c960a5335ec10388834851128e44408ac2419c11545a454014384309ae081134c7280a4488a2834d70f498a2474b74a424aa6015508c1091482802509100000ca15b230050e3ef045122457ae2c6099542a47ce265c48d424605a106614543ad11e81eb1e157b04eec12ce51249f3661249036191a47991c6486924824296c45d19736e5c716fbca39cd494aba405bda1093092e8c114b0608210106882132068c205110a9a44123c01657b628a92164fb4a0f404125ad45c1c6421ebe83bf96ebe8b87efbd1c4a29c5c9a45c49199f9a1172d9ac530230274ae042b823695eac31d65af30e49138940211ae91119921441c427df8f2c84e0c512296841022958f148cf277ee889f404d18b003cd004234f3c49810aa0b0e6cc4ae6ee3692144974d60f498a18743793034b8428526c1cb2542251150ba3ecf0a307c8040ec127906887b88af49035f1ce10fb995714fb9034390cc93e33a8d52022f6f35826623f331ce42407f8450d7830c4ca3e1f442413b1c7e6675c1299c89bf926110154aa5c1926499ac824aee0e11024923442244d54e5406f57aa1c66ac4b240d1349739f54255966fa0192437dcb7178acb7f908d28ed95b78646f6114f824ae604b864958e2d02943295b8654ae54b172da7eece063cb5ecaaa7921d6290b59226998cc2df7008760124814574592061ec80f901ceeed73a83f1d888f20cd4790e9d9eb331c28777a20f23f407250e57061a503d929654ed2c04b409543e92d6fc943b22021d695342f26894b8a9838ac8a1762d56712e3f05846b61cb05ff791bdf4b7f9d07c9872249234f0a5ac921762c158b4e3c22a99b3500d8ea48127521292d1ce00070707a78b8ae4c4142427a2c02b0f9d68c2b455ca57a3051caa71828bc905d769574a2427b65800921358d0219ee54206098a15489aaf165c18b60a5b85a5e2c805d65a6b8df283aed5a278506b8d34d04e815303807e48518ca2ac6042217bf1d5232ac5083df802a98b1b80a00ad64492c4942ab8200a2d8c90248ae088abc4a4d907a7653fad9c538b24b4053d37d69c375a5cf78e7216d0508ed4134c3a7f5b55eb871405c951762305c04c2aca0f57f499e97999faf99a73e46c39a7167acebfbbf18e72e20a82d173ce39b34caa46cddd9ad05d4dd213b218694cc3df78fd909e84d1a77e484f9ed06fd3616f20e7a5945e649f99869d592488b484135ace6091de118d47484d24f5ec87d4c494ee66ae90c1bff7e6a3737a80f5bec3d3e4acf71d9af47b484d28e9f7906ca0a4df7b74be18827e8fa8df2d10b8d80fc9061fe8ce46bff8de1f13b04832415aa28b3b97905937ef25cc3df2b7a3139e6a52c6083f7f82ba3ec31dbded819fff910d51dcf577c54d87feca97c21ef96b1f8f8a4a58f17833594850acdd74e87b203ddce429ee991adc2a161607f7e260c1b1388054989999e939dfd18cc581a5a2266e39ba546f97f749c91e2e5947fbdbf0760de70cc4d0d9b54b5474716180506759753a719bf6ed2e7f4fd5a242714771cf38d96d8f77c1dd96bb1ffde4b9671cd64e19e2ee47976ad3b317b91bed33da6c1dea2e3c1d572dcf7097eacc45477614c7a1eee2824d4fb9dcbee4eebe8d7b8ff6ed9cf6ed9ad6928d52cf8937a9bc9d5ecbdd967d527d3af61c79c2a59b300671aa29eeb4a7da5c605edaee826f8ed462c3dca3b04faa4bf2da6c1b0527afdd4671af659fd47d7c898784b75270f5f6f3f4171d7c916880a5610f171717ee27eed8e33817972ccbb89f5c5c5c3017dcd15f47dd8deb5a6efa767813d688e05af28fe61e31976ff7deeddebb645aa671f8943b2a371ef2900656fa34a8d2f632ab1e37db84b41c0383ba8637dcedf4a671efcef900b36cdba66dd7512df7d4025f4ed927d5f7946aed8796bb546b31b4cbb76f5785da8edaeeb269da0f9f9486f9a46eabb6f7dc6bdfeeb55f2dcbb06c89e0ea9d88b7a7e892856cc77d3dce535945f38ef7f9075f56d9732a7bd57d54dd7bb3ca66f9a20f422db32ad657dca9ea9f94ae6955b5efa9f594e629abf79a7e5272cc290c4e25f6092b05770d73323422b81eed2fd909db6b38e636c3dcbe5c3b9d5a605aeeb2d24ecf919aa6bd5cc3dd8f1ca75b98bb3c480cfdf216ab6936ab60320384fa25ab60de633519bcdce69797bfb872f98b2bed2d2eb8fbd136f5b7f57079cbbb9797ec936aedaa979f6eb396552f2d77798b8692a2e5b7529cfeb61f784269a8dcb5681c172395b252bcb4e44ea85b7e7a41f529e6e5056605933bae5fcee11718989717dcfd68f9cbb3979e9654bbfcb4b560d82fb84bf5098563fe8261ce61b9a1def37298a35e0ef397c3a01e93396ec76b15cc7b5cfe721897bfdc259ffe924f7f71958aab548d1f2a1d64286e35b39a31d342a5bad930ca49371cd31a713670a6b5d6ceb8b2d7464e5c4108e1ccab193a23f39ab8a217ea70fabd27d32f8543f3e45036e0e0ed7bd9b68083c792c8de4c66997096055c87a3f934056afaa0a891d75dc5ffb8d7465ccd68883b8ee338881d3b762dde64c7b08b5edcf1599689a4584e6e56f1984d3f9b66261cbc95554ec439c6f1115796051c7c8753a390c1989a31573673dad3368dfe409ab3cf88ab591357731557d3e62ecefcba33669c133e5a9a89136e90c680524a93b8586cb032d871c39ac0aa808349535e0d726cb49070b066467c351e8d279689a41d382bf0c1992a1df8d71dd0ec977588abd769efec6b763ba0d9427db34ae725f5fd9581ccf6c1e9eb9aa6699a9677704e5cbd63f8c217556db0f7debecf7923ae2aadb926aee08cb87af3869725cb0a3656e9ccdab465cc33421a9c5534f8f7eb5e5a1470dd8f3da25232182643e77322cdb706062fbaa961835d451afba7dd18d453dca9c5e5ef355e9fb5e2ca5a38a5ed2f0b1e8558c495cd4631fee091bdb5968b51af232535da9e87acba6ef395e76ff0603496f085c2d1c360da106d3a207a6347b4d95e72e28ec669f8e24d8c6fddbdf085c2c5c80f8316daf8f20ddf988a7909fbe074cce3860305343b2615937fc0e01de22afe05f31057f1283cdba5a5e6c4d99bb0cfa3716d79a87b495dba4582eb26b4d84b58fbb5dd090df596816c679996cd67adfdccc5af63114684d6cf47e3c193f5bd6769c0c15f99540fac13d689235248a09aa4a0e1514d5200afa8b5d62aad11463c3ac3a16c7833043d1af0a6072924aea0892b9d5a60fef27923e3ef36726eecd0f085c2f58d9a9c1c979cb8a3af5f6b5fd062682a2da5c95c6b2d77f6308f81a192b80a22d2c8cbe01769b6a7f085c2c53c66197353a9ed4ae9f218fc823bd8416477421a4876cb5b7e6160f0ec971694cb6df199e996ec33232557b051d10625a5a92403efd8f0ac507956b4ec0188071f3aec60c30e0fcf0e31f1467933f4f068c0b75cf9b878e3a2bd66f74969c1b075883637e20ad6481af8fb1c7989e5146cf6846db9bbdd825f6f76b33cfcd864a24c818b0dfbccf4cd3e5ccd8d68332367959952a57b33e7827fc476757b36acd6661d116e38ecab7dc5218aab187362a5185523a58f0e2ae9f8e2aaa6a3b5b1e6ae87e6361c7165ed4c012773772da45164997c58e4c322b108e15a4758d139b4d32d8790aea63b9f1a3d5bd3364a350d8221e9758977845c57e20a5ebf7cc4158e1e767874e81e181d7762b4afbf97e352a93f55938a3455cad24b33ab784cd972096ece348c4570f0d7159f6968a219a52999dedebd29bd4d73675f5f5ce1eca56726ac7dd20d9fa39a9651717587e020662d3c9da2294a21dcf7ef9ca89e33775523c2f48cc3d7ef33139ebf2639b5b8e250291920c9e3aa97f1155309299e4054c854319c1614f6167cc2a6d2bd8ff96eb9fbbc96bb95ad57c7902c59b248690a44bb660ce034cd3db314651062207f69f8bacc50d35f4b5a5ec5e3ba7de9f2ad195cefd96e5bfe1e8dd32f7cc22db8bbce3d5eb738ea2f9c1d156d2cec5a1e3f5b72677f9d6e3de8ad4cd5b97eda7ec26d7a4ebc3afb7853e9f43cb46fafa8ef14812ca2f70ad05d2ef849991e2c7336a3b428af983ee20aeedcb89287f335713573726eec7834a0ce9b0136a491c9c8f00ac1c90bf5cc85b761b479ad193ce979a9bd197e60fb2044b71cf373b6ec8b5d62bf2e7ba0dad7815c07aa8d6117fea9a1e1a341731e0df8b7951e0d5823ae646501f6fb1aab199f91b9b971f36406efb1dce96014bbc52a18147bc5e85fcd6a1553d15e5e32ea3bdcf73d58bec7c11e6b562b0ce7caf38b2ffee8c4f30b28837c576a0bb2578be0e6959e4d97f4a42d7dbdd65aeb271835ba7ed6449b4d877cacd0befe625e8936962e89112765e26e31b4820ac422c30f83b9ecfc02d2107318e35dea19ccb31933325996652fcfb2ec142303c57ec93db24b65a0d835cbb22ccb321e3127dcc1bc40976710d31f34661583b2e3fa8230885874a4e133f98c12356c29656d4b5b12cd2f88babea3476295482526c528f591896a9fb2b9eb409539c3a420b87a7aee10cfbe9d8c4142ae2b8ceee0b1faa61790867adb4d30e4157322767a9d57ba038a5ce936cf9891417dbbad96cb3db233e52a5850b122698ab5d1f68fd6b49587275c71296ff5043fbd8032d4db73d884250a7730a9e95d7007a9a4705abb6f39dc745c79f343d2d0df52ee2e7d5664ce5a20841042d8527a57d3b0e52f6e875bcbecebda6bce54da2eed7ac99a86ab8637c5a54ea315991413599458a5ebb948a5eb4da5eda8eb2f07a256a592cb58d9bc9041c823d2481860d46bd8fe87bc8149240dfdaa7baa44e9ecea538389ae578dab7ad3d6438b46224d7d93ae3fe56e5544d773b9bb92ae41747d29775c3cd2f55ba69f5ffc7a66350dcf2f24cef517a55cbfe495ab4f8dc6def9d4f0a951d3f5d1fecaf2cdddecfae6179086d7d75b361cd795f9059401aea0b0a4b59e43bdead7920195b63769987dcd9d8e7d7d523ff385b9a7f6b0f4a2592382bb57d2d01abb32a1c3bee7665bf3fd8c44a2d015250e64d092811049fa76ab08a2ae2da70c6bec3201bd909b8ef90c3e2b119e70a0e048cffc6ead77c12b9cb86a1961a511d0af65c493cea606b77bedb5d75e7baf3d96ef7df639f1da6b2f8ffb09338c9fe1da17771b68fbd9d0ce0d07f69bbbd971a0e1bcf7482432d2d73e8b4373ab8d0ddd5b6bb7db4fbb236f5c501da0f610a74ec0755a578dc3b4b7e9b8f916775ccb182464ce0c4677f0405147f6f6392dee9e449b2a90867b2c432fbafb91ddc1de728f7d8f4c33822bbd333dbb95863234234a192876a69cd9cf6787f6f0d9b35bd8c11963a9eb00b59f33c32da00cb00a94c1fe3e273ef8a465445167d04adfceaadd8ebdc32c866db9e7fa3da6c12d200d2d5dcb99e89697724fcb3308af672d2df61d6462da63d95118672d187b767a55a67409d3bea450e100467bc787bc9152db6a4470d8378cc22e7847d2d8df77a54d18267509d73ee137c5ca85bb0df4b5817b0ebf2429adb6d770f7a4b47d862f86bd6e37dc6719c3f6eaebf00986fdda706056ae7bdf110c0a327a1819f0b8889d84f0f214660cbc2e2284d7e19b81004d1f0d7a0afbcaddcd1348bcef600789a1ebdb1870b564c0e3a2658ffc2d01d7751323d22a1eb2eb65df9e47c3c3e249dfdc4df99aaf46f1ab924477f31d6d807c09b88ebeab97db8e773390d8f3103b5d42d3dc75cd9dd08e53cb77f5f4170b6b1fb6093ede0f4a50fb6d3e2a095ee0430146a8b8738b2657bfcd870c45b801d66ff321b180020ab27e9b8f002ca1810e343090900084addfe6e30512c820881794620c8c5481075498fa6d3e60b28052c40ad76ff301b9a0c1138c18224b161676eab7f928428f9d62203c66d7d3ab72a8768a4016f6e380ecd881f820d4f7465a8e9ecbf49229c35b092b5dd8687b8e2cf518655b86dd84b36fdb2bfec1512ffec9d16364fa6b9f206d94658e6618cb742c0f61acd20542ef000ef39841da087bf6b61c726908636dd947290f612cee591ec258d96353cca300dd196df71152d246d8f6ed192e9dc3a61c38d3b33cf4bad06427db88a806d68d187ce0d178dd3d1a6ec460e8c9f01ac37010f61f84584e4a788893120f13ac2cdbd19e137bd8cb030c31858565223630618921a6306189217cc0c22ed46fd3d16384fdb54f07da6888b230981d5e8da4d9fe24cd96834a197b2f89fb636def38d3b9d05d1097f6793c3002086dcffd492e0765a41fbc2b19920fb074cd403c64cb0769699f69e5062debcf8e36447be919d5e68663a7c80f422c209cccad470d5d0faf489aeca5f8520e82495995232ca9ca12b8a4e81d5c12d4c407a1a6c7b4259ccc3ebca3fe79597a82b8b44f0c35300294b6db4d37e5a0b8e53f56a94a6fd94864617fac1e997b7408b57c1054fb3c00063fb4fc43c2424525af8129dd69a0071a2e0259d9310c44646559259fddc83dc5407462cbe3f058d98f294bcb673422308d886ab0b06718cb52bc2537be1811796f06204257c3dc1191f233a8c727521f212857e06350c38c0807a60c6ad822224aeee1fda0a867c68051d03b1153129329865836e6470c65d23d728fdc8b8305e7bdf7600c2e92a1bff7a605b27774010060a10b47965085298260650557ae932118718229529085122a4358b10208193cb4893d934008951015112d69f97824dac0cc34849e27fbfc630539e129d98d437da7e6207a5969f97b1eedf4f4fa4c4af6528658b21c344dc77ecf53fa3ca5cfcee3727a2d0accb55f99e7e5f5a70c935c3fbd25432992e694836ad0cd414ee6bc769efbf9129396976eca904a90139ed34d7721aa77394c862f39881e65ca41252aa567c7f28fa0df8ce5def4960c99481af9535ecd7339c894e5a0a0ecda4bd987132d0761399585c8152492383089a4c12225490ac74a1448c4ad2e150b99d43f16cd377573ca8acd1c953f56cdf749995912711182f146b928cd074953b01042c112466001116a20042414a1829e3ea87271c03092740516229020e292c0d40f690a932952928684e2e592a4ee8aab226924c4468000925578c88e3c6443d914cfce9110c38ec1456a600d91d991c1127305c64a15794314c355aa9cba1786e352a92b3ddbf40cc7f512c6177742fa76a76353a5628a5a11c872c261979881f0cc3ce62f7519333162d0cc30e9f7307bcb3397ec2ecfee12f10f6cf8fc727a07afe01efad7998dc14e6606c2137319e789f118274078548f790c19790e89c9413157a522a616f7fca8745a8ebde5d8df53e960773976975cfa81dbe38643a5833d88ec9e535eb5e9918abca992b4c315293d481af9981c2453392826c3214923ff77af0a7ef7aee4777008e68f058067590c4e1d629e98a772d00b8f390c0e1262c11c6e2a07a41ef31c52f912804b5430fcb1609e2363f010f844333a8247f0c90b8f79c3dc118157a02dd430779b045e380761b010eb6d3d68c30c6fca3df574c35a0e22bb5b3500304cca1852c156248dfc0b180621dd753009c34164775c0fcc3103c9a63189bc79ac2449e0152c712812c5227923e3f2990a07a90e44911a583b324fe1a0d48188390c87c141300722260f89f963bd50430ca298c360a298c390e7559828e62fc8c0aa1cb36f268ac94ee251c3fcb16092bc8951248dfcbd416acf67456a60c5b88cbfbca37aea2f1cfb9337414431f21019188e00440c8ff1577324917a80a5e56314d106b67c94126d9e76793344f527e32fc67156dc5357bde364e49ae7481a79194fbdab317a920626c77c089198c7c843318739cc65644ed2a4f0dd74c8bea7f8a7868e8f0117a98125938190b98c5f190899a71ea36420641e734cf6cd416ac76c249337a0ba5cce416a774249f2261573af0c8e9333f718a53e9fbda386afc768033191c6a177d429bc810dc43c9b180e35dc766202d65a2bf4441347f84194244c60640b4e38010c2b6444822d9618010b1f98c008266c21b3c4576b34272459c1e22a81c5364a5bdb7282176dadb5c285a44643c9b4983084cefab59820a5bb99a3212624e9db8f8b56bf1617443d807e2d2e92747db1898ed10691094f3ade4dc7bd6f46fd7bc7d5563fc0bd56175674f7a4d8faeeb56ed8b0e3864be1e04833ce8c5617ad9e33d7d4cb772b9b5a6bad3932dbb857c0b1916382ece2604d439c7e477289a491ef082e91371287e6ae66f589e58ebb96b4bcac226f706eee706a72b7ba22699e4a71505669d9c9243df070e83962920e7202b34cf268c8c7a2a424db3146180487583940b6ab2a1e0e7540b66beec11173100b828e87b9ab4670190eb164161afe69e1061390bcd56b696d6e30c1046ff5ba7b3d68f989b523b8970520a4000b2790c2fae58d3c8ebc61cd2a4a68716185142aad2dacb4b6c002c605e33df5217888131e7bd387441ce48467c33ecf835d0f8ac77e651eed3207c16b9a1157394ed2d8df84c36319893f718ff9839cf0c01cc267e771027f659ed44d39c8f454de8139a846d2d89f1e934ff630c83ee6a089b14bacbd84b71ce444e58079ec3cf3d8fc95836091a4b1300739e13986c51c048772517ccc4130076d8f790749631f54fa9334f72d352e7fac5725be83432f7f2c8bfa63b1e4861fab5f7090931d540e822ceea7bbe021d910273c59864b866846a7cc65f844d2587bcd491e12e40487f758581e426f3f6fc2f2252d1eb6948045978c543bf53940d6631dc9686770c5c297c988359a8ce8df6644c409bc5024022304e35e1c2c3857e69c096306870a970aaf6a5346814bd96bb7d6da56152b6dadbd5298b2a505157df56b696184ee66ae005b55a8b4aab42e14bcd8b1e363072e742d75b5336ad8b042d7e7dc2fb8ae460d1bb5d656165e74adf78beb45ced6af9585166dead7ca22a9bb552b8b28f1384e2d5b3d30d232c69b2370f1ca04ccc84109afc8b69beccd741c6c1d75d111d200ff2004f22001e4a3010fe44102dc06d202a38160eb088c06821a37a794474143f94ea83bfa17bb6220391503d9a183682d0fe5df1086f7dd1ee8ccbe62ea6ac62aa6a664662aab1c8ad3b697664d5cbd1a0e7523a7bd49ff60cd40e681b0a4a196a747433de7b3425f7b7bd03cf3a623b10c754fca500e1965a87f2496a19679062843fcdb70a00cf14ae0de3b9c777429c9b2ecb5c5afc544ebc80a2d2563b21caa6625086d5763d65aebacd76697b66d5849335d4e869376b168d16eb4a93183d15e361c36df2a5c3c7cf6a2bd68a3b5bde93330da8b36ab1959c4e00521bc16425827a43ca58e02d9acbfb64eb9b79bcf386ede8a357aabbd68f36e70228db420e07ae6b377355a5e9bceac0f8bae592ea2e9ed6db4a99f6db3f9a9836ea03bfb20b28d38a0ed68b988d0d0d1922222231ff0c010ab88918745122a1841155b4c210a162ced46822a20052c6c810b5dc842451596f6202259a8d0420a58d80217b2b07ec826206ac1c004442d18b0b4ff20c4d2f27b34d9b5bff366c15dc75e710f967f8474cf756deb2888a69f0f8bb6cff3f45e89bf6fce09ef03b2196d7ae8253c32d2f1940a303a5ec6dc057594b1cbf113d415cba61dcd3d30e6b8d7e24e888edc55b8edb85db37d8ebc5af6fc3c87b5415de3760394504e1c147129a8342aae52b10994424ac727af9a97ad925835f30e15ad92190242acfad752028a7e2d25a2f42de256107859b050e465c952d07c8704412129a3944b857b0d121d8f684961e4cd26a2e8f937ff66b48b9b13357ff498d4f2b74acbecb7892636c842bf2cbb7cdd74b25f7b4aadb5f6945a4a71cd53c700fa6110a362a8b76b8f508b259ab74cdb6cd0344dfba663b351e9178f9d7e9908c67272bddece07ebebd6e66e67eb71dd29ae5cbff8decf393559ea2050df164b37f760bfbf9e414d628f1bf6bae960bfcf4e9b8e3cfd8629dd28a53477178737c1822488c085143958c2c2369dab6d3aee766fb8781ec6b7e9d034b831607e62b9bb3acbf70281831708dc83f6c2b32f9541a4e9699e9b0d4254cc8cf6f5a01ed244fa6d3db8a1be84ae0bc84f0dad1da8f68fedf76898b4217d2ffe89a1b1bf0ccb7d2fa16ab57c232715572fc0f068c07357b8ab5f2b8aa1ceb344053c420d9ef46b2d81a56ffab598208a3390d2d146b47105d73d291d758836b15b4b54e9d8eff81f366ebc2bd189ec3dc5a4e3ff56af154510f70a4eab6141068515997e2d28a640e1041448ccf46b4101821948a8d1aff58451eb09142ce00a1950b83029adc089234e0c51a1d445105ca6d09488d1af15650819595a4d985a4d4c6919d70777662f150313a314057c09b83db853a21577b66c0086e98f36a6431bb44c973765c944d36356343d67832e5ade546fead96e4df31cee4c2f7d9a7e2fb71ca663f7d1861e5e87e92d267cbd055f3765d3b3aad9adde84e92deee1de73b9fc741376c12db8333d9a1e371cdc73a4e9f698fefa843bd3332e77426dd2d17e5dfbf5ebf7267c5fcadd9334c7aed35f37f7a4b6388b9648520207fd5a787079f07210a069a6109499e27a1f840e29a573ce5af3a387b2523aa794314ab9c99c83003ce2e984973df3315ee2207b50aa74a95febc9143a8b45a48ae8a80451c78c6a376a7a14ae39e06acc469c74a88e31e8185b50ae749c36db7e2d259634cd28d8b234f3ab913fc86436edd1783050a249c7a32c40459197c5031984252ee2460469d2c1d4a0a0b3892a99b0b7c92db9e0d24e803d216a9406712b89301aa545229e0c75dcac9412b05a326500ef9929be806b72d2e07541c3160d8a6680cf5e4b101482d12a91a1de0c40b21c706658218494d2f91e1fe214a26091444f28a4950416fd7210a06ba5744e29639c5b8e997310a079d4bae52040f7c4c7c31c9f5529f3cc90c2f981ba030e66c6978ad1c875892fad6a554bada28df628b59c8a2b793ce5cde566ccc193f8a66469c34fd260bf6e865d0b44e3155a4d92d87ead24ac682581d44a8289c6d24aa2889ac88296ad2482903177f7be80a355be43b53cc4892be9022e25732d42d145aa6dca5a6baf55c275a94e5dc65a6bad1d5a42cb8915fd5a4d9ab401fab59a44d19f49c515a5d7e20bb80a243bae055c771ee20f48036cfa1e804e5faf3461d2f31ad4342d7740f3c2807b58366302ae9be96acc6b79a51d9ebbf2e690896843af6938342d2e207e1e2517203f9f7dbebed54ad7b86d5955ba2a0779d371903765a12e65550ef1a6e3106fba506bb8e35acb7a68ec1b3661f519d0ecabfe566e62793335b2526afecdab6e5326c67c0b6700e6fcd5ea86e355b71932e69ab89237abcf58e10aee109cfcada59bf08d34d16a5b49ceed0f68b6965118a6b1d6473caf272b05924deb172fbb50380a3194134a4129a41b8e6cf18dab253e33fd88136f6e441a592f14dc6decf0f8d0e1c68e6823e96c1843962c59ba86c027fe88ab78e7bbb0eb0fbfbe2aacf4d27734f3375f0f3c3c1a7127aea20d71156fc455cc39467049ae124745449a6873145c079fd83925dabc274cc028308a0a8be838e11168448b3742244d8cb7017765924542f6b0b4bd8ac7bc3dccad95b6974850e9ebb309917d9d53ce9c9ef9d97a6d76e9f4d057e7d67bbaf5b04f7de2ebb097587b34649ea547837ee2ece513bb5ce2895197387b346eb65b8feb9b36717cdd74d8bbfc62979f70cfd55d377dc3aa97bfde52dc5358f5f2edefe52d8f46ea13a770375fbae9d8d6c3be9bbf8f81790c56f1b80ef3eb3059f572158feddcb773f2f53126bef7f72f3887506f47e10efbbd0beeae9ed35f67579bd8de9f70cf3d97b9b8baaf2f619c99b8a2572adbd872d8df2c1357af51af70331415ad115927a4e33bae53dc14dcedeeb7879a2869d8ad3fcc3c4803bd2ae0eee3013e99734e3a218455e013f828a574b68cc2b832e0ba9a7ef30aa594d65aa717afce2f26969a51ecde7b5fceddfbbe55df9a9abf5a6badf53575ce8b8299d65093d39e69da75c46b427d7157a3af761dbb5057a0d8d7c5d21e6532a9ecbd2ae5bd62c4f79348c729a3b440347731e2ee1dcd1cbe37b388117733d28cd24b1fad124729637e94ce78652a21e21271efc432df110666342dcbb2ac5bd1ed4a534a7734bdd174fb21d2d829ee5e17bd6537ae320d0333d8fee26ea6ef3d2ada60a5ec94eb6674dc36ed36a8869fa984539166a6e9e1fca5c5b9851f1cf2f5a702d80ecd103f47eac89021f4827eaf4f8b7ef93d1a517bc2c57ead191c0d2132a7c4387ebaa0cef13edff33e67e7c4f8deeac1c725a2b5164ed933f76815c2254532326fe6d1d61c145738ea917abb1212575a53acdd6e75abcc441b683f6dc5281eba038f742b8e0b8733574b01773fc306a95ba8e48cf5c4991e758a6804000080001314002028140e874422a1603822d696f10314800aa0ae466e4615a85912c41042c6184300214400004000606066d80024132398c883c31950bdf94ce8a706ca672932d2aa80d63f9df2629f587f04e79a563c0a6cd8b1f35f1b6f525fb94155421c6c2989f6ba392c8b41c550801451617286d0b3f73a29c7c4795f52d7803474a1959b744f10548fa9b08c4d153fe02f9fbb423823648398a69d2e7d68d8381561586f3c3166e14f964644c22e3d9704fe5e124693c4afa97de22f81004cef947f0ce01a31abbcbb27a1e971ef76c1a6bac6434f5e1b964a7a79a1bcd61723deaa4bb4666b51300bac1da751cc696c8e66801309c5cdffdac6de0ef181b802822afff89ab8cb0ada58c93c7dd6272718d09668dd4f02041670517327c070c72257199139134e4bb4be71255a99e60f245402a4b11c679d9b02a9f970ffa344697c6825b34b5002d6a75ffeb5871f7594c1e454363ff9d70a6859f0223a20f6448762a25c4479b935edc7cb94443fbf1e7aeb6db43082b980ede54350ce310da94cd54fcba12c890a6c4e5262698e93bc9bbfe91c37e88f175a5a2e927f0f18772008dddd7239fa160c7b9b3e2127de106198e3f8151b83e3a435cb1bc2597b6a03e26d3a2ac6b5673c3b2833f897f44e8f505c7475b570679a071d70b933185ad2a2ecc408b9c8f31061e20622a165e3d0df57df8a7a3b7f21a1b50a48aa36925ef06c7b59c7587c21b095b727a23a7405eb98911788a60a3d4ef782801065e57e1806cc135740b428f022f4ad3325b04026dccd26ca219390c2ce3b64b87620651844bee43c6c947c28517d43738111644b871c8cd9a6062042cee6a1f1665146e8f2db408425b080e1a2a887f8413ad26b003d20339a4c5f057a71b7ea050f3d647ab6e9a3ecd7239296afc961a68c46d76aed9b747fb6041fc241284ade634d9b46dd6849c22a732f040eec10396f19240c0c22ba8251aaa220de4428a0f984649c1242543592f53a807196fd2900c7869be0cf7f6d47b856e2ee514012c58c93a7dd085513f3bd6a3fe308ad671a3ec1cb386e949b9f4929f07d4ef709d71216d2ecad7fa28e207a2c191f1994241ff99338a11e2917e051dedc6e7d515d719998483a8d311e484986f70571460b2b5aa636c83e19892cb59cac0d416aa3f9f6659e1151300e8cd95865ae9124b94f28001d2aefb5059ee78d1d3dde6c3ceb94f689f6e22095ce6e316faa55ff1227d2a26723bd614bbe93e71776ab960e93ee49f121ed254619ba29cbb3d4a6ef822b435507e01a074a781bab9169a2236ea0680f2b148cc6abccb2604aefc1e50f2243315dbc68f510b59ffc6a6faac89d2b544c72268ec14ae337e0c30761cf2bbe231dbf81037902d7dd044c215e25566d778f80840e508755aceb7717157650ae078164a744f89cbe198b4572067ad80adf71a36f395b9493a02b2b19e9a6dbc1bde0e37707f90bfaac856230af17607a62ad193d41bbc1636d838f188e3b96eb0df0f979cf3d2c4a2321a4b1490690c42222eae21aa034367bc27f0d092083c5630f1ec095280532e0aaf5ff310f4588fc0579d87320173b132cca50ebee56953d48b1a9022c5a44c7d150d2224ac34bbcd9149016413f375773b7c79ac77d40371edbd0de553088d9c6366d257cce33061ccb1f571ebfd9dbb9cedaabc440f137abd34d50d03aa2271236a721c0966e269d32d806416624c4740aa194b0a49af5eae4fc14b4096ada91041711214657076d400be01b6d5d3a5791eabc336340ff3d739c9c829f4c30e9c84d7e1293d4b151711dc9e0c89cdb735e7b130c5ce3dd912410cdbb7aa0bd5ace880af8fe6503e0b336eab976733029900539e2da0ee633974db284965f480f2f0f642501fff4bb55e464b80ad9e71f54535cd013b68cc0e8af5fca84256cb670dec00c0d4c21ea23113ed12ea602f062a8b330f5ec20dec0d73929fe9ddf836eeae000ee829429b6a85bb191d2d3ea8733117d6f3fe4ccdd92afd896d36f97163335829ed319a606273997d22c708241d339049c48eb4973e7f7d33d12f3f23ca34d2a721eb89b97fef943490e1419ba54f90b0303bbbdf0c08145f3b0408b33fd31ea101d3c1cff2137f2bb91af1d2beb12f0cbd8e2647af20c74b73c33b5f1c8dbc0f7a5c984ea29e6cd3e656a06f301c6f6f36176ad4f7ab055b5f14c904baa61e5e72430724bf5320d80dc01f1a15e7b7520b0d1c06d28b165f0b9492d462f57d41ad83f04e739291c0471010c787bc57d9503f017824625e837f5aa44d16250ce6a19c1bb4eee8092fa4c9b14f4af9128b6e6dc3ebb167b2f7b23622346a73f4fc8a111f3b390888133dd6b1967d41006b154176f6f6fb75568823e8d3d6340e84d2416d65f53ad2e32a5ba86434f101a263b4dba5d43f7d61fbd1ff56bf3478b4e27043a2294142750914b2982b09247a5fcf18d90d4e8fdb328cb27c7cc7c0d6298d16401dddd57626a2080a0415d64c90c6c9040d24c9f8246ac25f760516aa8e0c01e9547d4b5108a14fb17554cabde961741c01dd84742bc7526c69bae8361d639342485160dd23d41832ab9b4e7b713ba1372b022e3d342da14213341484c123233f9b090017f9afcf830483ef9f00dcf67a9aaf11a74751320d44e0d1fc3e90a522c37aff1a134a141a3ca961073d06cf063580c2e6abe6c8e54650141633002cdb9c7627534b7ce5e490d5949007662a8bd9e28710cf74b29d4be3611cedb4e593a8508120121f32585189c1cfab8daec1e472d80f7b81efefff8bf00e88e36cfb570b386e46b7eb0bfd3e820083a7de5056affb4fcaa082be026b61786140f71e99625713447a6034e750fd0b5d8dcee8b71847696acbac20a44268d34612a177b077b547f02003188a2c7658ef56f8ccd0b2146a13de0a287e4ab2a3328087a0d8b4f47aaf81baf08b3f8b36aa254b4252aae05ce01294b2fd3f602c5349b73cd48051cce53e15ae796a816e8f17c8560812a4054080520e34066dc84596a353ca0770de3ae070245b191ae3097ec212adee19ac3648d2fe00f95d7d6db454d11c404c6a089360151a379f839ed3a5b5434ad78a6812c9aa5903bc07755e11d868d514fc42e15750b148a9ae55127ef3d9374df5c00d902cdfb6bef4acac82308a51923cd19500149c16ea5a2d10f74c79618e313331639cd84ddd4a22d0014bc061360793e9d1a894f37b9e3da6724cfe23fdea8244c429fe5fad8ab11edfb1f410271330d0af46818c9a0257c68e532d72ac09ec19341adfd2602ba39568007b55554f6f119b2aacc405fb36da46591a2395752dc2a26c5f50c3c09c1f6c647335e3fe1405ee2f45f155b41b83809c76c3957b7c62dbde18e72260a54e6128a4d132146c19974262d608980542b97ce099995517ddad9bd7f2812fdc378c4eea942b093f454140a735973413175697206635333480d18116b1124c4b2662bbb9fd5b4b968c6fc2ac7a4509cba3e236b924d624ebb4f1932314feb72e5350dd02bb0b6df1f0b7fb3a2102618fa74371033630fb01e50988dc809bb763dc8eef8925cc1eb2e39aa084765e3bc3a4ee3c5c86c87bb70abc3b61af77b6cc63e79d0674859f48935fb0a3e931cc15cb27940a6aaf5cdf75e06454c07860d6ad4784102b2a5635c566487191ffd55f50c1d4fd3cab89e8d74d1b9fc49f21a55a1598a1876c6e1e3a189215ffa8b477549113ddcabf19e7094b0461fbc6e944e09340c24a82038d8972856c4509bd025b9cab12cba0839db06ba63c3699df3a5388a3a4b61cef22ba16f125f1ceb2775c73d48f379eb855450e2d86b462e430391160b8dd00ca100741e5a48eab8e23e43cfa30c32fb53de0d46b0e7a36f52cc2692d3d5503c9f49c020828273c8740361480c2893af6ee602a1321ef99aadd04687975cad3c2144ab13135847096fcd258f291921850004108dd3952cb3194d306ead6ce7a9be0107be2ab7c04ac0b665551d0ed188e6eae8f8971518bd27a97603fc61daac09b931af9ef86ba854954aa84b63b582dea6c4c0068fb3c37490fed1b4c61feda700335f2a5e4b99af6f10abd92a2f9790521f24c5ab87677188695996eefd47cd88c45a1e75ac7e12b45f51398385160bed9217ab8e1bfd34d7162cfbaa7554b968107e00fc83c73930a90619001f287ab6092775ff7c8516a783c096704ac7dcb0dbd6e36f0c62ea03d7784d956e2a4aa67c5b5138afe04317bb6e41df78c7d6a6c4d0feb4ae895539c43245b260d270b1d79f11dcd2ac2672f8524b2768ced5ee35850c686826c170a837b9cdc69ca013ba42a438a76c835c23223041ae8e3420af9e4573d819b1e2c0e3c24ba80c312090f20fa8544f6481a1976b15d89245a9833ed9820535baae355fb459851348b6287b36df1223e189fcbf84078f06a8318f6695f8b35a05b1d1684b01c59be1e9a1418e9c624de331a23bd77c8a09ab5ca0846f10d884bfcaf184f255dddf0685cb0a5f9214cd36df0310ff92437e9a9861f4521155d7426b536df74442ec304ba6b659df83013ad04a61808195c60e8324b685f7b03060a3d7fd7d94eb537a5d3656cae9615ac99bb6f708e1f6715f393d6c6181bec19ca649d4b0fb4c1dc86bbd6901bbf1781ecf050380662ddce0fd8284f49ea097b0a5947998e95174ddc829d34386a402aeb912486813f8a16e5225c2d702b5570f0b57a5291ccc956c202a2c6c56c24e9d9e6df90fc008333ffface4ddb3a0b1acfe70aaf53abb3fa52c6e297b81573c42a35a6c4767cf5c69e9ed79e45a0200eb1b99841e90f644df93c1492cb4f8c8e9bb7582d245da82f839dfd97be48289d4f7424970ed618b27d48507938ecae9ad0823b1d1e2365b161abe7b416145491a076ef848dadc3f48a52d90fc9a64071ce124a1518e8a76776db41f6e4547ac87d743254663032ff59200c5a44fe9ff843ffc1f2e7b2dc2a6b6efc09a90bc6b88fa924026bbe662d72225b111d082503cd08a1260a5e5bb389df4824943cb032a190658708450743d0e6178f95ba85a9cff7187f6064ab9059b96b9824d4bca2d815bc5b8a400d82b3141438819b84018a99238d3284a1ae40e55c9f9cdff76657433b911c48aeeadd0e5f39fe1bbb5f8f935c4ba3351fcc3139bfbab50b04c6f3613457e57028b6e003729f1d41174df0ec932b13cbb3f617facbd08d18e63901e7371b1ce13d834492c6cf9e1567e83a4ec326aef803eb464bbdd57a024970d1ca697b01769319a6551860e7dacbae5d1995f27fbc435a940d74b003c2292edfc95ee628867f12d942fc7ad3584f17f814ebe7ca781e5d904ae46ba54292b8cf27cf5ec0561234ee603d400302547a6b663de396be45e1d654c76d287ee9d3c12a7c47bb491d18a62acc2489945bc65fcd9bc34653d515fa08ba34ebcae3dfe76680b0f8c69541e3a69ac440961a08426f10f8e0b1b82843f781cac5012e68ffd90b7771b95fcf6fa5d4175fd2473def8523fe3f9602385f5f7aa2b67a501e63e2102f60b550d8bfd560677569d088080f4026b306e2f7ffd2998a0d55195493633632f7e49412bf5fe47041c8039298d9779dcd828999a8863189416897d5f407d73d966543ccd76a09a31f6277e20a5cbfcc50b18ae543f68f846636807aea9226f1b36031ff022beec755d6ca75a7bbf270cdaa9d6553471e30e4f25d55a820b8a61357818fef94c812e9f7b2a2122381c18304098115e45d0aaf1d4eb0cd50f9bf9d3e9706deb2567cac9839dc55abd1a0bdb85b320a274c2dbe99394e5c06bd450e6d72b840c10deebae2ab48b570759dd18a8b587e72bd03ffbe0192d85bb6b69d296033bfa32f3a937d1e4a7374a42b48152749911804763cf221ff390aeea30013a634c39729cdd713ecb82d6c9ef2e5273e530813fd8b285193854e84d55adc3f6ab673340a361ddc474fca4f833fd81769a21db4e92ffa79f6a106b2d02dba0705528c6ccd1d470b68cad5021a3255ea284d8e52bcffa7ad1bdcad6dceec9439988b1ccb8334b71d40a15f561d48ce8d0e38df71d9a9eb954c70e7da1c912c6dd66717f4ecedfae1fb8d70493af7f64c2bd57b32826973d663ab39703e30b7829c796ec12a236b665472632403604045e5d295b59bdd930ce0c55651657cafd0094fed41955e6b3f30c6b6871f165f4bebda8d6835935d3817db9cfdaa5c9247f07242c319f7bc7eb52029b7791f97f14ff2728e74434fef49f428b8861529f0a7eafb2337899b8593f10df0751611a1a7aacc8c6e6065c0abac1849ce7f30763f70e30605aebeb0f9f84f845c141857e445465a1f78b10f868d0554f95b03f25ca823d60152f1b0b3de3ade326ffd2aa6c4c6a7d24723ea6f651e79c82c7a3382feb0ee13e04d1417600a59ac878675ec8f0d833c18e4b0d63b2184504cababd2f080003d49a8a818f625023c7b08c80deb93854715db4b7a26cdd2def77af4c06a2d9e8392ab7a8501e38603fcc2fd9c17520bb190135537ea4bc49767ec731f304250f47a6c8f775a71624d36f1228918e77f497af80972cc65862e01a8e3d71c449cedda82374fcbb675e2a52e38d66e9e0a0afd66572208e99dbe2d159fa3f4de19b45a7ea2c37ff2bcaba265878dab586f7bc377f68f59039b2a64c0dd60991f37ce33b88f73e94b4d7d988e6cdffb5624f05f29bfcba82040cfcb92fcd2f1f89e05f57ef80ab1cb3bdde78110c19426b3c004c52453bc5f3d0147463c19850642560ea3a2a315d70936e006ba5561dc1ea308b1f313918558093928f48532a3267e9c2e74b3375d26f60c7fd75659bd7f3c414406710b9448a90a39b8225f0d74e44ac4d42512a694741fec9a93f87e7ac6c0e5d81a8bef378453cd47955fecd31da7ad3ac288bea380c9f2e7009fa3af0bc7fdf348db9b9184abe3d89f3b86855a43cfa90e7b9c887796f37ca203e418e791294874e56af19712aa22edb76dfc95d100d9cb8beaa2fa25804d62f2c271ecf2ba0e15bfbd4514aa0c368f234008d87750d8e4a9f3882cb53e639e86561b7ba9100cc384713427e675ae8d211fbf29ddef8782e3909091f1523bdb4f6081598b7ab2d0f66ffccd9c8d998275d4b8a6a3198335ce3724dd88c808e876bfb141512a1784916b26128918ffe74834c928e844dd6c3b35d21e15f0dc1e951fbd9c79392b0267052878497e529a336ff5dcf39a80c9a282c1e1524b6136f7c7200c06f612b5a20ee12730bf3e47c9a1e28d15af56931ad2114233d6587a5ec70c1377dc89a0ad334c232b1b0b918acccdd01405ac684beb45aa655b685a8dc237d06448f89ad067136cd77b67ab4e2f6e7e8b0c8a5c12600c259fd2ea231bdab6f62348ff07abc367f95af7ba6fcdd3ed3be00d6ddc4ffdd0ed68d6fe35955890cff8909650248bf6d2f7f10d4087306690c130b60064e3a8fa01727eb65db1fe265009c1f9f0e3fa99923dd6c9f2fb880ad082d27c41e677c54a71373ae15ab0a1494e5b8eb0f419075f0592ade4307985cc3cca3a047aa606b1e41842af55264b13ede73b4dba2afbea12702b80f41c9dc145ca4454037300a52f3ea365ed92e5be894b36bef9941950783a9b367b4919b7bb4f07a6db6a37a49a9f1411261d80f5b1dad5a29b3f07df6fc1f9c19604447b463f96fe0c01634f71e350911e6a4530ca6ce009ec0663cbde31d4b427ffd1cfece17577a93d409499c51c7f1b67aa17ddabc01370380f638724d7b768b118cd5d879ed82608420395fc71465844c4a646fc9ca6c66a72d0fc9ef2e49e3bff2f8b87ce319435cada367394b1c09747a1134c22b391a211a619fc28327580be676131e89cf4fcffa9e44d7bd482e1280e99a52a8c69cf220e469b49da93fe43446170fca09a334c6629303ca81f1356c2bbeb9134de0d8c65007d7890272c6bde244a01638e2341da1bd661473bff8b0b5c704104b90215162646a7c28ce6989c38d8bdf504dc4d7b6b243c155d431c4bb6e5c575dd32271715d5e01db740eb2f37b55a49b26424fab49c0f8fb929686094a79f61789c3e0295211890c86e93a55eb086ef133425f80b513aec6dc1f6c9c191893ba538f16fcbae02b7d46f1a90124b09f96da91d7a4ac2a78e315a5afdff68dac77f50b3f9ded261bb06ed647cdeb4bdf5851b5537169fa66bb24d031713c35b39864a4526074e6c7d6ec9f87fe11f478e5096f745e1055fa3fd4d02f40116dd35da9628c14e3389691685fc01513ed37d0f4af51178f6e62d545b6a194e229b0fdb17a934754d94f8d92e76c21a663b76959e87aa6992024fd81499351569f6c852e5ee045352bb3197d04c24344cdfbe27e23a5c085be4f9b77d1f532b9a974008f0da5677a46e6bcec55898ce7b80e74c1efbfd2050a4c12716bb689bd1bc777f5f3ba440b07dc026374b570a0f8128aaf3fb227e6ce234e68013fb47cb0d5c22d3073863ec2331d96f92fd929bc497c43981b17a97af3d1bb90a971f1718f5ec39c7ee899ae7fcea58b63cad433fe0ae9babec34f125de5cfc5429eca217fb0c96d2c20834f9d6cae1446e2fe0f68b4d812d74aee7a6d705d4f40b2b517e8ff5672e8bf423ae895f1a322b9dd784c084ab9a315238b223b7249de2a9049f6374d203cccd0c1183a5808b51b2bc92d7266e827e9f2b35971fb7327d7e82b88f36e1454a322cb325f85cdbebe25bf72ca9d7b92c559a1b2d01c8bb415b0b815d84ad76b5a16bb98d2efad2eeaaffa667f99e1665dcc111a962a5bc3c61ec20e3a99113f159e97e0650b25bacdccabcc8b702ec732b9f48e2f79a20895f94754831ece7a86d7851a2abb2dced64320c02e10b0f14d569b87af2d3a17440dc296f87612be60a84a76cac02d2e3944240595efe8a792049faf9d5a8ffa5d000ba1b54c21de932ae0b3b358885cdfbd2dcaae02c0334df33e79fe9e6abff8533a72cc76481addcf05a90717327e854bf498e91483c870a1104930c7c4a7b0a2c38bdf74e10fac0e3471abc44ba90e4706a64a31432d7a64508601008b5a2d91b4b74c09ab8aa8e3ed7b46f340c6f324c79b6209cbccc40aecb7c16e53a2e9400436b03afad8f3eb617fb7f2104116a4b5bf9637ddeee9c1a8d50a709f9b00e3a58d4c38a581090d6080c71260a819fa1045542051ab6faa62ee222d89fd68b05bc0b08f15a2ec26ae76719d9741dc167eabb8eaf472e667ee818ec3d802be5992c5a6c428a71460b84dffbe4eed726cd8b85d39a5274ebc053998dad98fc269faaf072390eaf3913b3aebf5552eb26795d6c84eb1efa7c296fed60cdcdfa843c7ec948a6da7486727208353292f37157b96d08b8d9bf48ea35c404491816108acce63d4e3a68472337374744a44ed653e41d7d6593ef0a03be5520d8ddcebc29dd0f86487c7e9d3258a8cabf23401dac4a61fc2c9cc1e1c34994d768a4facdd38e45f6bc387ce7e6cbc4523f42c68780bf23b0ef180d5dba121ee533604091cb77def840e47f5491d50a6b7c2adeaa9b2140f5a53adb2856f270eb1470e7ddebbc884f0c0735458a0e26e58c555917d3b35a36123a16ae41b3386a83a4e49ba80c27baa5a78a6e3e1ae31988fec1eb7934374b763b1e53c6c18e2086a95f9708d91d15d4a5a1b83d1b863a80ace10dde2b53e7b8c94c23bbd8c42b4c3c2c3560129fd56d991731c6225019ca7a00688f549d9ad08a57adfcc2942d029f6a198af4d9656c9ebedb340ab2d3ccb3a55476d787eac4809990e80ad4772cb178cf8be360d23ae025c109eab5b30abd17a0508779c55fd6a40ab7e8486c1c2f6a754792f537ad708ec708d1ebb908caf9b9218277e225a787ac476980e50a99251b59a0b4dce5e16b44176984c73e4c245080601603bb0d056091d649e29200b245cfb166c20766243a4fbe777911bba9a9a4737479014dc5fb003fc96b32961ca405f68e205ba97fe0818868a40090c903dfa429e647f118742d6d12fd089784b3e6e84abb8d5e75bf9de74586ac5ed92c4b6d53141915aa46df7eba1809f8baeb64baa916b8f48014c6ca7955e9c908c808927d32f91f13342ba5a469bdb1c799cf14a5345864b5a52707b7f06ce1d912cbab734b5ac1a3ce8f0fe54fdaf1c26fdf12b6e0dd85480de4fac4e55a58a696a3398d18aa31c87ac2eebc5133a7bc2514a130ed1c466ee718b0996e27d041838544a1a229c7299a422a5cc23a8a29e4256d4683fe01a47306e5d0ccf9e103d389584908958867117a956af4aeec589ebc8406fd0821bcb8d722e1ca0d734a438eff0b05599940d4e94a299dd39c521bf1e1d99a6ee41dcb293e3bf40cafbe08dfd07e145231d1b03461b76d3f157e28ab2f08f5fb38f21ce173c978776c436585ef4f6f071ea2900743ec91f2f482d2a0ffa258da1c3540e59fdab53681ae18321cdc0dcab87d2d8b80611d251c3b662a43879fcda4e13f25856386326367de6bf313ab5b4111fa6191166692138a579108dc7cf2bc37054dc8ff41d40b694d45fd2ba886eb017c107c2a20f1ac2cc609eb8e774432ee09ba209f6d909ca3da4598a9909f43f133c248d801cb4147470a7c74421f4a40c678aab771fbbf3b6d4475f16706f98e1b2545f41007e482f0930a14b6eeb6724de35532ca1114876c81b4c5e3dc4eac47dafe2a257c5f83651eed3ca038ab363d8191826a7e07c6a74cc54e00e2d99c99a865949f79acf7d03fff309e07aafe12c0e9d7714349c8f625a6d29e15dac4181b71811cf5d8aa9ad7669967e99fac8f3d2efc7d9a2d727864b6b2f5e5a139405e09d18e7b6982620d6db0de0b710f14e987305e4478c32fb6653ee1959ccc4ac585a054dd5608ac261e50cb051542667ab4b5576a6a5a66d957cba07e7137dfdba290ed86a9f826ecb8dbae5a0361a7bfb7dde4cf22454f6d3c1e3d5bf1703eeb2122ff180c5330915b252a8cd129c8825003fdd00aa5448ed5b1a126427f82d645940aedce6c84544bb21c6891cf9c8ab5ebf8d32c207801c872ac25b51f4fd81ab6fba9a04cc9120370cf67a8eb708ee7d82860c977025ff300b503002f9d2a32a25f154464585741b8dd594b350ea4123b9936a1b089911e02aeba38e00346d2468ee495909e03e8ccc10e4faefd70793561aa51d6ca3a7910b441f77811df148555c71b2422c451e12a207794f623112af4092d74dca27412ed9454fa357af5a0a1c9eda58235dd295af9296973ffe13fae2d087b91ff4bfa1bb6414898e56113456ed0fca7754a3529eeceec7e76949afcbc440d8b1f1582cbb6fa24601b871adcfdf678b7a46d0199f6f5f3328f5a337d4ed78496504b5046a9b503a284b6bd9bd5737ec3e5ffb3e29524d2664e8cc919e336561c0aa358b3be7ccfe98562f28674b6ca02740525730357aacd3562a7c61390b6785e15b4301cef1ada47cdaa6474da633b30a79e59b92e4c95885fc89c455df61b9fbcb4e0e79be6242e3c18c6ab0bfe28efa8ab269a6000e4725591561953b73c952d895388ef2cd3d6aedf52d47e714375a814b1c8709fef3541ddbee33230a36fa5d341be70520e66e0e22c2e018ed0dc01addae70a4bbf2b186c0b4d4720063de304aa7a1c615e30b9c21def94bafae1abb477e3f91e45cfef9ff22f9906591cc210578d61df5be2aa31b1aebc7b92e3640b3a3a272c2c407390254320bc9e81cbb9e24d566abf73f1853c377ed310951325199ca95dc8ad5b95b495ca301af36f98d6998195250c0b668d45a46813817d818b02fd5263ca83f081d14a4fe4df656405d1b181572706ec726ed34b2f5ef499427d4de91c388ea66a41fa53155a0e39881cced14679828601825c04722064913630cf2e27fab4f3d827123f542aaa04f514aeabdb9f68c04474c3a781e18b0b898dd2b57c643f55494f7eac279f657c2d44caa89cb55c5e61f987ab0b64b8667542c4d11a84a34fbd81aaa6ac85db7b9fc73c9f8cd88768d68392783646071d5a04c471b2a48064fa5ee0a8f14aa0728ab423df88328eb070cac034e7a529e683082b89a6cbc36515cadb97b8480ff0f4c4e67e25fcfea841176f5911e2bc282f6b17fb6dbe7101b2b34dcc7bfa3fd6bdd8eb368531d7cd74fbb3d539e234fc7786f40a32537efe5c8c54b1fee58177897dfe3813767764d2ae40114d03c5da8bad743fc4c6153b1d95b883dc3a5d2b212db15e64cf2e41e779e61277bb62b4a1132708d3d9eb3823bad9149c03721ad9b42e405403f456a14e6b3d75f89facdfa68571ae057497a1243d98938fc2068f1b993f2aaea882504f673564e63af88f9211bda915bbb459d67bb797be080e86eb3ea97aa4d3f38bbf74d7779d2181e456f93f7743107631d09de8ecb154804d777703c9d2f2af82b2834856f8357530d02c2bb927324bcb82bfbd8d34d819e3a13453ed0e363c45ebf7a5bf6d105b1153e781e0f9119505b30466b41bfbede4fa4c1c0df014ee7c5af7e7b07c5174dcd6983d9cbdbd848f431883e28d186f96c5d9673cac4e9cfb492bce0b1b04a0e3b5739d8cd4e8cd4a07c45eade122a62e48896b165734891258735ae2b355932938a940252c3efb8b3c72e58ab40d9d065556487404ea593ad1fdf609026d6838a7d0b3275ead246597cd02d3cb981f4dc759ebf154a5ff235c591044973f7c47767ecca733b8a3da1828b7bd0a950d548ff0bfcd49628cc422eb54baa7cd438d1ed9b416ee6f2fccb6108b29668c1ae1e743599a82609180e4aaba0394bad553d6407a26931b208e9ac7058342e363019bbbdb7a2e498adf88fcafb05c7ef2d8ed09b1ce17883cdedd5462f0d4f192db74f506337463f3a5819b0512e5671414e731a7fec56900160e9222441760a7eef373e00d90e546b34c94f4d5e837173437d3b163a727d17d344c304ee7ba88024904e5f3ae7e2b184928b50c5daddf13ce8ebe6155adcaac74f844763bf15ada6bc5378e36f117f9e92e29990513ed215937b19ddda3e7f2b67d8c7e741193caffc8a072f23c21d4feddea5a5a02492321fbf84de8fd2702c938dae4c3014ef7a514ab6e00cb879fe5cafb76639e512fe80bfc1120c3cfa762e0730a150fdda057a61d39102b2ae580d5f2a40898dc7fd01da8b416bfb054162ab248496a2ea2c77d2ad63f81c38939f7d09d7a1465467e0f15ab956ed0e2ea11cd0fffbe0261b618f7166b76d8c213e35e06e26b53b05dcda687b72ea1090420ec7e1a3437568c3bd41d8bcce9aeb851810b9985d161bf49c99c8321d66dcee3b9e29df735012de2f204858a634b3627139b9499d4199315897ac396659145a632dcbabb7a96adcbcb3c6a6173e8db21b17d4627a6c989afcb74e8564cf9cb0b5a3f1a66c5fe7dfbc508800120090b281d9e2411165080c4fcb32470e68bd63e5cc2f0ffd0d0179343c8d603af1503340590ac930516878cb2420be37c00e2e119338e72081ad239f64d878150cea5437e91591f1fe0b24f7628cac66757ae10ff6b20ef4c1a8eadf3ca06fe767c9e701c996828d9b4715249d98fcc7531f0a1d3885ad7c09e7de5a318b5f7e1edc417e14bfe27827f0b054ce09c1ba9d0d39ee22f6a273a0abee078a6b9ad5192099946af129b1dd484d59722a9c83341401198ab839a390b3e851e6ea01384dcb0f75e029a5816dceccabeb2d2ca8ba4e1cf272d02bee3b0cf7ab5f4fa5fe6b554016bee01aa68083e883e21ed8c45e2af6fd268ea3f5ebe15d1ce816a4905807d94c8cdd7f747f89b6c1ee46fabab32f38d5e03200249e76a75e763b8023a5caacc72b919e1bec147e8d567d6991714863a2470c323b811dc246487a46fc085dc01526ce8949c6d6ffbbd3c6864b21e4cfde2012e28bde8dc55fed540a54887d625f7ce247e21fc279fd34587573a0e278c1443b0e5352110fa2d1f118a45cda88be3183b27fc93565a0e0f2b683f4384aa61a034458b424f23a65a04f11e7c651c864458ef5885411eb59a0e4059010bbc52da451abb458e1c44f0d5813ce5500bd91602a8fcde355246b432acd73c927cb596c7dce73d8a6b9f3fb9eb4fccd806eb8f877310419c9cdff6358101c5d782afba906e77c9a0f3fc8c4bba01f8da385e07e2cd6f31b083632c12ca51a1c8918c71796b3060d99c0ab8f520d94fa9b98a2581d3a44504f6622179d113d3be2807e2570e9502c8326ebb848023f4df7adabab2f68374b5f10692cc48b5f2e2eae04051df751a0aa3645345220f6b19344d97e60b2de4c4a825488324446ec0e22abedcf19b91cc05eeecb24205856fe9d6e041b3e7e4df0aa61805a87d8e4874bbde77558f0a5b0ab869c16a31dfabaf36a2c8a664011ec89ad04053a177d822f991c9c119beab294d7ba984ac0829d03d8d6c9f38c7459d1d51292ddbc3ceec758e02b87783daa8105d65ca8f2b331ec5ae0df043bd6c0677ea08da510b6d5a1cb70a8234bb83a315c7ae41e280b213830b5d383293ab93cb86be165342e9d58f07714e8214faa951ba2702e5dd735e241216ab706cade05cb68519f99c6ebcfbca274af9e3b14153a2c427a7a449d39129513ae65110a9b90c00223beaac12fe548b059085b8b8454ab4e8216f24a405858f55814023c0514709e848cccd559e5c94c91b44afc2be7d05a4b22a2bc2878b8377f46f75fbc0af9f66e1cb018acf85aa80c6d0edde480665639643a1799ef4481c23ad4b8ff38e13280018ece9983e4e23e4783467032e21df156124a130557d3ea4877bf973c8663af40df06d179b48902353726034bf9a4966e51f55078ab775dcf901765bba7429cbba4ac9f4782329d446ba2e02cd038f462ec353d7914f373b9e0cd2039037729fcc59cdf5edd0a304074b09b7b2fc57130e091ec89ad4bee8c18ddba2ec16aa10564e1bc082be380d8d4c817c2e667093eeb4e4bf4701aa0b9a4a5ffb26124c273720c30c91c05e9fbc4053020bba54a5d221d345834538cd7cdd91597ef24b5898167d599983213be2ec53ea6536eef0301069d1682e7108037fdff469182752cb9ce8200cc4fa25a461d50f83300092c265dd4e9864a77fd58ba49633c902f3906a4c6bb354955d4ca363b8f97d79bd416466ec683a0e9589c5f27d1b2e1d9a0d7db67073dbc821374514b0b60d3c114740c650a07cb2d5a9ef8b67cd0059d1a2741f52645414dd0552d7bf05ff027878e334794bc1ba4d15d86bed4a0c6ac0fc4cccbf9308e6560daaeac914b8fe34f5004112737db5b67025f11fcd4a8b16b6e0bc6345d91427f1f36c7606f94d0ca26e4e0f0cdc775817e80190d327a0a902fd3b68d07ec59884001d467e48d4836c089a8f3094a28ff09a6a6e044f2b76fba5f8bf1d21147fea5b1d41b20ff046a2b689512ff04fa218c6c41bb0e2fbbf3e025de297fa524207e141d0a8eb2c11990460428f63a7328e86e090ca87fcbe2346507065aa805251da97636c08e28e5d5ff0dfda24f549e186bfbc24e4c1938d4c94b02833a81edbd214e72a8e995bfff195f8c5b9f1722df60875bfc1032ed5955a42b448c97c4bb1b338c7e8f42e3585f1cb107543063718d672e0760e1217bf26c002e84d8d8093ecd1a8274c35de1e1fbb8a990db00c70b55949fa8ed5372114804d826606deb2016810debb782457b49639d69156744ea7cf92ab67f39af0d0b67d400a55473a5bcf80e7dc9897ab302f857770496154fe572f20df407dabecdc25903bba241b1de18ca7485e653658f2586062b5a72ec92e0e760a19632cb31037fce782b2c132eb5fac2b91eccf55a9934b01bb5ea93c68213e5dbb41a2b53a6e667457e21efa58e9cb5d379bc29b1ff5e9105bd13a2f1585124e0595fb83986496ec34729a04af94e430ca4890514068234d522d97ae252a0bb43469575314f3838f54623fb6d2b772241e1fc741597f2758cea50add295a39be4aa34d4efa0a38b6a9b9a998285a327121896fad7b6e238a0aada0347dd7f6a098312656091c2fb1f59072b2a7ddcc4a14a80d82376a4c47e4799f67cd0b0802444818d55cc3346015a1188c0d6ed0be54630ccc5478e2e06429884f624e0a4f734203b5b49a1c2212a6498fb1e1f25abfa0cb430b7863631a3f3f1134de7044ae44bd1b9c9a7edbb903885bc2ec468dc00b7a2cf832c785d910dc340b71d1edafc46012a6c88e8c84af2f273d7a5f16669fd082b89ff65b37e1671bbe355a013f0bb7458a84bc62ac97a68e8cc792fa501f198229b86d3cbaea8b6fb365024a4c7c411baba8456311c030dfcff28934e57ecad2820d759787fea87a05503ea818000c97bd7e9eff563ded23adfc35bd7a0beb27e83887d57d31f41a4cc63c67a6ce1e43e6a6a0e82d1f7ac26917475bc91c1c333562b1fecea27a608a3ea761ff2362a0fb5e29ebf090fa0c453d3a76e71b14cf1d5faa58c7724665a3e9268bf025b309cab1a985df0f7b210e37beb02d0214dbc860577a2858f5d000ad75f4eb995d12b636105e85b40d3ec60c72ff8a162de42991c6dcf37f8572f5b8227e8564f0b3514f3d2240706042472ab295a4101ac3c79bfe643da89c9e39403e4a2a2352d36d5bcc9105d7f2ce5676bddae9820eb49bd864e412dc7f26e313bd27655465f2156e2ea11c3bf8ad82284aeb4cae26adca408002178b79879e37bb7a42bdbc2c8d4eac9ba642afeae1b57b1702bfab98ec9c959731b95d14316fcc897d20b1a18c4c2822bc4264e00290038601bfb1bf71491077215768a4d9d6860baf6c30c02ca3e8f2e677fc3f13bad61fd1d48727140dc7bcdf5fdf6816d5874cd1ba5a008728c619ba12fa5aa5f2d1ea1ee619c1e46e33854f42d6b2b15fded4a49ed004440016de8b7fd617fd12559b391a5f2d4af27715bbd6c87de5e55c9631be259d0f4695b9827128abf8d31d069691b887500e8255fc055c66054ff86b836dd3059869dbf45434e07d240e6ab034eec37c85ca926614a7ecb577e60af586ee22c0ddf32e5d6a8c1e752e9ee6620653a42bda4870c434405a3ddfe504f076528bcf9cd1b7447ff847b89a40d717686aa24943ee547a0c64fa90116c3af38840f7567a64986e323295016dd44b09dda9784b7dcd541d7c370064d81056efb41cd85453672c4c22afbd818a03a4be7a3222f381c49bffc0b793488a1edc81b43ab43d385bf65a9363d3a2c5b936042cc31398b711f8799e7db8db44b8677fd3c7d8bdf0321a0a19ee132f377736a0107380da032d9407413d982869c4701cd9b05e1707e4f8120b67bfd2dd5e285e864d436ce5334a0b6afeeee2609018f19fe36a0bf600667a437f0bcc9f6df8f124ac394cb6248f8c398b0e72358df6946bd503140474ccbbe23027bb4c9d666b59760e4d4cd02f6e93dcfe36b2d7baff106eb26dce9ef13477d54c8b42db36cc73551f395868904cb6e551232aa1188840fc905813b1c7aefee7333f963ffa2df3d1d658fbad58d91b840c8f2b5005b90177c20515a0d6db93e2ca6c6deeba96b3f1ad4fb1e708890f89f4bc45613788b124b0031ee4c1931880f92af14be08ae86432b72b10ddaad32f30754cd95d97b7de5b254be18a276f8cde9ecc6e98576742a4e89cb0e47a3a50368b541419d8924e41142e953b5e2aeed6afd499addabfb1460eda4aab89d2d1a33388e629a280c97f4a2213128b32862bfaf3b04b2b48a5b57a0997f5b1beb778bf0a116f7322ff256ff10f0db8038edd29ae84b790ce4e0ec28382d322c19414b3e6364c3f369280b9434a4cf23e34a8a3b428e792706d29cb13d58940cc65912c6f5cc423473a999907c721d2575f9a4164d1b241eb10d4ce0d7266fd18a186b86d30991e159468f8905a0bbed9ead78a796786f101641c24ede93b44057d456d94080bc6fcd0d1179dea03c4b604ae4e9bd3f60e7badf6f3f6637dfe1ec51b6d5bfc10f90e9bf04f220a80a45ef2d56f16eb31d79df495373c6bf99bc5d4777c3b5a9d6df5876b60ed366f70db525c01711f69c2a84736c2a8a790533b4b0bae2c95ca172f28937184e16ed545a5e121183c91515cb984a31c80cf8b21076e545203c934c755c77cc8813ad688ea368d9e385b5d4f0effa3f03ce0d15615a5445166dc61406bf679bbd73bfbde03fc6b8dcc032e1a61880fc59ffe039839b603c748812c50b533264fd6401e92dd0a86d370ddea0b44ec1ef736ca869617fbc4d3939b7daccabbe0e4372a9f3cceb70d7bb000de74851c3a93e7f3da06f1e75008deded2b010b067e2751deab5ae31f1cf160f4315d6073cb000850f8daf481cc726115baf3871c739846e8c27480cde323292f2bafd4412937d58aca8a4a4a25eea7434de4a2cfca1c24f8908fb07b9156b610d9c700dc6df4e572a86811eab4c14edcfccbc125aa42aa468eaf6b32313ab0e5d721fbf528396be2ac8daa6cd59fc24c895ae9e70ac251c6f6c779c433dd112d1ea326affb137995c0898ab255c77c6630bd50c0c01e563d76e7923ba873be2964e2f385694f5a657bd3b6542a1e675589e2f3d9764aae533a712e58e1fdfd8fefa97406ebc748471614345f5be225384debed4d72dae3cc94805f5277fa2e87f7608b917c94a4c6e4092c4d1897656d928476ddd44aafd5345da3cff8846355cd17cf587b43186bff12d44aaede9c4e720fb754e8440532e53ccd919458331d35127323c3020de22b2bdfae13ae44c9caf5e0de979b7f682d63269a30f6860ee529b946d941e20af8075e4c627ba42dc76be452d98a95123efb66eb2a32bbd1142e98d206d73c2dc7d407f0b6f11d84bc8fe74da4130dff8a2423dac195f14daa248b8796d4fde123c24c6f23b4bf4ad20586494014dadfe44e4249a65658f80e3e6d6e0e2e069a353644bfcfc5043618583d7a7b554d16c133ed41b4b484283dd1f241fde6d735f815cc56f297407762d9873729a1971478367965e155db1026e771576a61f86e6d2cded878591b47ba1cf85cb48a85ac904aaac73f017adbed62df98161a0d7c2f569d08e9cbb2d3d51ff6450acd877e717488502f15ec5f3d1c1cad6144ea29265706218ceb980e501c845edbd5ea19497b89d8060b555cc647339be949d240d4d26445e209634c5b6ee4b78c790f72e60f35e50675c3fc4fbe1b9b80481bde448536657a6b40f151a85b2a12676afb2306bf1ced3f6d90827e9a1cad382fc2b0c7c55afb25361a9bb3cd7d918460b5f13475db3e7c60689b1620ead25c4300634a1d5fbcedfe6660c4a5f1c114035a973b420a84b620af886c6bb85415071b03e7337711b9a031daad8266fd8479145f1d6e273d0bba4739b60909ec99fea9ad42e922a8ea4c501dda060b23eaa23afe44c26e46ca6d0ab8fd459245c9d0dc612e87c71f8c13222fd0cb968ecc863e44720a745879a94ff063563ff69d693ebadb1afd52de97c89540fa53b0a2e8f8585231f2847dc6d161ee03bad1a2ad9f86b03774ae256fa94bf2c7f095a0c37ce40c2706ebdbe8d849fc2b01d1ffae18099a9f1f4b0ec8c66a97877c20ef2895305bba4ca4eaf3754f21d72113fccb7b2469e9ab70b6c475a4c15db32f4975b92a3c753b624405359f9ada4050a72bd2222643e830032c2f522e9652a87b86f4a5b637049d2b616b915e05f102628a1cba6da5d81d1c7336fb23c20a7ed205fd3a1edfd4d579d1097be03fbeac8489208b06626b65b9ba63ab12b84a3d91a962c859168c994e3d59c8929673862ac662b9c1b02fc6b0c4bb7693c96f9c64c166f9a4151c67615a3801c22c3e7658e9723e15fc4caf1eb82d32756cd7396eee8e9dc6b0b5c67e1ffc03d074e93ce08ac5f8fcb71d2987bba4c19f92958a17a5d0bd805b735004d9892f6ad3467c8e45ce62e88d157f691733670ac927f9b35a9c8cb4f28c75bc416de9812b642bcfcbc5e4fbc99b10c04cbf524431554bcc9794cf789842007421a7fd38510a4b966dfa453e0a51b91d256ffa35e1975bc44363c189a6cd7836e39e405e37f8e593c987311c20ac0817b8cfbe88b101df9de4f9ab2b56e3ca4b3cb484c2b219db3badd2a99363418719f7ad8709f7eeff91912ddb1b0030ee11ca20f9657e8c8a62f3c9901cb2f33b01546451f2a137c7ed1a115ec708ad0194071adb8c94a9110e49e1b230ebd5eb9568cebd14326998bfa25bd2b0cec31f269dbf64bb885b838684f7d22c31da6369370d1891337de1050a99f2fd57be39c31b54ad95ae56b2d1ac8341dd8229abf3374691e73b61d3f0055e469d2fe4436657f05408260589c9ea02db2e5cd69b32fd82521ece16140aba32333ef48f091b2b0386d892b571c1991fba20e8053014b8bdd44d9cc48af800b50c9454d02d09b3b304d50d7383314250f41e06c1d91246d65d9e3e57d00db1bd5849a4f2eb576146902ba14b07b279535dd8ef4d14eb1900ed3de95975e1447566cd9b58ef3d079743c8fb356bf6f6571c630219f6b8558bd3e11c925f1819a1f33df1dede05b30511421373c39b66cab1a09d2d864a5fc53a4aae346a315f1c5acd6ed289dbfab029c96ace62704c2ad9b879fc171e4d9b5c8f3fe0e61047e26a19c48b92f918e41a53a50256ec551d0c4ba746ffc4f9c57e2e59a9f52dcb3de5af6498f85200cb9ebfd4b94d647b7e6bb25729e5d82915f9bb9f0753140e7e9b2bc4b11cbded1896c819e8322b6c65ddd444aaa14ea06bcb247319663a3e189c391e74ff6f52189d228128eff9b5890bbcde0d2bf1c542658d34237c2a483e45903e66c0fe08ca70319455ff671d353028a18945049a7f9b0263bddff2dc097ae236303594f3540945f5ce0b469c7c48fb4d29e28614d82833928af4c562904ef13a58c1cd8d95b00818f4c15eaa1329dbd7f240451b311b82b5c0a351019efe8f7c49024401ce272360f38a4979fdc0fa7fa07fc5c31b7f3d0b4cadf82d536df0810541b7850f3148d3670b109a78b14ae0b305698947c6294fe7418ad2f920e63ca6ba63010e8a10570b89fbf097c00debe9dec1d06f37bcc35a4987045b2295053e7337cae3fca0949e2e814031c7b61b8bd510599d03a1942bac721c137378172d2ae87d360605e3119bef294c8173f089d0fa6009a5a44daf09efa3d2240c652534662b6bd4f6e32b5156f670ee7e0bbd08f87f91af4606f22d2918a732009e7c53483737ab713df924fc20403776488635cffed51536ca535f06fe7e385964a6888f98aada938d3c2f0cbd60f071259e6da58a14e51244bb3eee01867af3f2d37a88a48400f621ac2e57b807cce4253d211f11590a53d83633e320d40ec01c360d313a3a2ca3873b98093fe05b3f50218c6da19eaa1926bb8bb424c0c406291f0bd5a93fb0e70073e6a6efdfab1106f969d565e518d779512fcc05c345b5b2e9ade38a5c7df2ed9c50c222015dddc0b68ece703c7e7b374c4e0d5767fe12b597613be4c76512a1408f9a2da38e289f69526e7cfff6b7634df175d380c498603059358061a72077fd1cbcb8778c54715283cb2ded1231b8f973dea8c1649e1a05e211a55aa03195342862ab1375c547a95813490a0288f6c60c4e97b254965219e1efd1b8b2cbb60e6c24c621085b5447147f52602d4074eb25be4062a6a6254c3ba0f26f9af436cf6128f6ab56cb704bff1b26354513d60ab31844088c969798c139c9612545fc1b58a10669c10bc38f0fa6702de3e332ac2f8b296d15966589e91dc2a4bcaaa24d61adccf04da16d82050c8669c9311387ebb43da2558f8eeda590f60f82c757d88a26c095fb97b6729a453caa5c7d1a6e583d6cfaaa83699c518bd638423b0ce8039bbc4000984cd35324724f67c98a73087b07513d3c0f7b45199a0458289161517ddfaadb2f0460e9e475787b2356e20b250cc9693c3cfd736220dcb7a45b12cd5768369c8f08dfdb8b359f93b484c377f0720e1ef1370712a3ab8b02a1864b1a99b23fd991fae1527643b21671a6e0e022489ae2069084b90309af9f500806f29af0e983c6236864186edc005702bd45c589ada9c47c97151a6619b77cb231766db850f1eb32d992720d2864b26f933b3ed0b66119a9c759bbbf149e771c387ba039f1b0050f99b2e825d3fdc49d6d316be8f22527bc5baf517eca52c0a106d533892e1287e7cc2f9fbc036f032e05fe0331871c67a5975982e41f21eadea1050093d476a19c0f740a344903eaf561452fc0c9809b2d311b62c500b1d94bf1affaa118c37a5b60c0df1e7b188a85e17a60c635f6c63561dbea57eaf2ffdcd39ca2f5a9b452a1f36bc88db25b0dbf04b345bf3ba7f44107e2bd9eb75791856d6736ac4e9478ceee8e4711a588227e527c14d1e3db4d7a5afd804454b1ba27ac9cd9f9b7cf6e9f2a5564bc1342fe399a9e7865d31060b76edaa54bb76874c31f5295ffc658d3d00d332931af28957daafabec565ace8062cb9d064e8cb8755724f6588189134788349e7f88f502a5179eb2b2adcbb6c86c17eedeca67f15df801b43ae475e0f9cd80c9e8d3e3bd2c7ad3c4f29e196a8a4dc5c313820662bcc29d45bf2893fedca288cbcc3209e24780a47f2aa38cbc05c7dc7e0a52e07f96e8fa6498eb4123a94ce506aaeaff98d3516052c0da704ddc3a18b81dcaa07871e0631094229adb3509eaeb3374c0e1ee659cb6dcaf53a1677c61b31587c491f1515eb5f95a0ddaa22d5d50a956d7afca2614d2d4a9f164f677ea266f0673398393fe71d8e7f276a840de47a87c87b800d1c7ad0abb6dd1464cf61e2cb98400459d0b2cdf15a0d75621eed2d5d31b4226ce0132b7e63853829f09023653ff70d5000d8d20e0a1646b4389fdd1a97f84357c1767f516e44aeda64ddf1b20859711e6d22638e6f4b80b96328bd3a4798d1d773e924cf7ab1517dec0301b0a1f31b69b27020790f650434f26233f5d4e8f1e588bcac09761bc0e7e67063796c9c8de9cca8a605404f1d9e6f0aae00fc72cac1e1337fc6b11ac4fba0d34e00ae1ff5e57bbebfeccec88eacd3041321487bd28d366e3c757c24d747591eacbf570d6d9fc8a522d820e4b7fec63593d1cf4f4fd2eae8799880d160c553802b6eff3e11811f13280d839077dc20139e1ba2c87908f3e5218fc441c6d3d4afe17229f7bf5c4cc96d521e2f7d7659726a1d4aaaaace06697e930f15bba285e1462ef1d1e5ac72f9c3d06dbf6673a9c252a699825992e4a98d6c223e22b0c4281556c44d5c4d94b847e5b68a5d235007891bd9dc16afda97d03999c4e69bad1f602f8a893323f661060659b2b92acde797ec579f932500fcb90f6ec9d03af50ab9e330e718201a60ca174be795e4e11793d9ffc66023a3f464046041629ca7f204ab60f881496bc0e07a6ef17f44199bb06d7a5901d2a8a5d97139794694bf4159bb9712630a1ae32bf64a995f3901aba0caa9ba822abb9eefd04654c1ac05a98d89911dbee44a7df2c903bf720eb7c237d251926d2462931488cdf255ae74819fea9c4c9a5db9057e7ef7cb552483e251c1ca7566d052e990e7501560af69b2e64c0a67f93fda0c33bf41c8377c669d580f8df11873dbd96b73e4941feb115fc8ffa399fd2974a80b16dc7c7efc9c1f9404c5df19f6cbad024ea7b5c27999a18250aa620336a8d6287673ec639c6593da56df02585f01953e4c250684b7f7eb22f4bc0197059d662ea0a2f446ccd4bd4cb7115024d019adf25db7215690c84b91de3b2de55e8b81e5f640f524bd46ac2d7620ce369e08f40f03b103a1a78f048252968835dd9688211d9df85d69bdb8480ffcadfc584fc9929a75cad66e1a45b45a9e4f103e54a53136b52ce8143e293d315b30e0ec88a482c1aa0f5d91ca1f8ca1adaf88ce7c1ade7e78bec50594d85018ef9b29fa5b43cda06c518b241bef7662f0bf0d688213f1a0002acc15b90f6bf244252787000d100f5bcfe9cfbfb0ca04fe0e2b28282ecc35a5263ea47826988a9bd0c7840c77cc0094eb9cdb42f54adceaa52cae5e71068437c1d823d08b7c334115a8662b19210df4d6236b8a76edce508528581e3e459226a8345b810f5a6767feb94ba3425b7fef2f9849f10a29d38112ed0a7f7319cf344295e2d15ad6d0bc5df6f5cbbd9a15efd6a2d2cdf1c0914268c0696276950a845a68dbbaa0ad073ecf18bc82a513bbb41f75f85da2333a31234c6fdc238e0e2333026bfde4f878d0e2a959c8ad1a41adfba34505f9f8180006312c9c76c2f3145cb6a2edb105f576b4d00a8e680e60974204913398b16ff623072a8d74680e506c4c2ce0823a1cf0983073a6a1d79b5aa828b5d46941c6da3becd12b526e05ea03b257694b29370f9c4242c3eba58c2bea350bd096d36ed1bd18814fe8f9d708730f5a95a1b4534c848942b0d5afe6b0e4c3b4e240ab3820535426d9cc91fa768c2072cef6c2e3974876049e5652404a01b1a30d1b6ea10c1b3565676423a35e556e6ff67c48973a8dce64ea2f2b0a85c1a5764d5a133cbef4829499a31eca986c23f747976ae4885d68321a1a3efeb3cf837ca75b80362d44c2c24b35e3027233dfcedc7886454f878c263bbadb9a6b2d14a509726de03d9d5e2697ea29baa90eba97e1338057277c1a47fc9967783e50b9919457701d983f5d975d7e9e96625af4b363b0a105f98a46c7c4d1462e96c512b6eba705b534a2be0072c5d4b79838bc1d00462321f73393a69e9229ad579b0ebd94e684dd1cd17285a22589bac464279606939f7163ff84f23d89872501de8e384c756a870ea921257283441a9d66d73aea27d40b36c175999aa7b89109799e34d37b00343075191c07e2bd0666a4f048aa147176cef496bc217b49e6418e7873d625db27b5024019e62d2814a9b938b38fbec75ac90f9eeb594e114886ef54db031ac8dfbd139a44be78c833edf67f49945641a7c5688b3f7fbb3ccb2d85defd264ff4ff17ea174d4542daf1ba18c8a2662bc24584b6c2318d652c6add87d6838581abad6849d76e161c491484746c1b2af16555e84f043329329ba0ccaca0b9e6318c0e6ecb7f2f1021570a0af49797be983b3e3939d15d612d1f37643be01e2e37a680b69d2f5cef2fa318d206a073ea5490152db827edfb0c728a9ebdb347ba44c5840f4ad2e373b38013fc53030fc7a1070af8ac893f75c50f4030c42842ae213c27d105355b728aca1541e7a1c4e71de0d773b0e5e577fceb291a9c5fdd13a648b323a7720ba895615b43039aa26a6513ad95060f4d115a55ed11a16669c9fc3321008f022cb2214eb7aabcf0bfdd4f8ab67767fa2cae7c8ad368437280187fd37a148d912fe7a4cfcf835a155e430bd5c1d05514617f1ee864dcda9a7ffc47458b04114c125219db72998c0675a001a0e7801b082a90439a38b28252500aa5009add04c98ec9723a858323922bd4f7f02e640fb59086a4b585f90095a46a5fbfd690ac1e09926d14df4d8a35e07c945a7e559ee97de1012d9013d9e94c743ca5cc1fc0036cac060e6f0955a573d8303803d6b952187c899183d23bd44afc00b92b3364c589d81d13d3a28441ce211d4ebeecc5a43b7c1666df2ee0cde45eee28e190eae9c42103d995eecf7238a4e20ca760099fc690ced8838d7c1ff59c3ef8e2cb5eb6c2c90950d5051b5f74bdf1ac58ae0fe9ec34310099221c9ebc793e31e47bd9bc6386ae60bb05351478c1a8ec157fcbdd3f3c81dfcc2270f977b18173a41933e95c34d82883aad2988b34a714aba1d7b68ffc06bc4ef7e0b6f4945d60d912df7627481a75fa874210accdad02613d9bb66e42a12901484a650a638442f3b21de652237c435afe5ac25ecb0d0cf8c155ffed204618e280bbb83602da1abb7c709c7a7bf2a0efe72c5934f1af54cae1f127fd9584ae7eb1c004462469912a9d2ded4aeebf4f8752721e2dafb70f27b6ec0d93f2c9535651f4ceb9a2784199ba86b7237fc076169d9a1bc8ca371087dca46c41f54aa14f2981617350ea4c70a43332683b77a5a3da63b73b5c04fd0c3ae8edd19bd50e42fdefc61ff6eea3f1917774924785af02e88bf11ef566e1c7121722752dbe482e9c5e7e6a872d3acf65dffd6817fe6bc80f858c981425fb2c9b42429cff48cf9521d2e6241a4ca479dd8fdcc32b2c09158b842b1a26a701c0012a00d876fdd1bfa137be9927a907283e912ff7376406568d22bd929d1627393c0c825a40210e0589a560e9eab6c10e980a95d441eccd6c8201a4d90a0a1ceccb5705f16e295e2a20ec0d04eca2edbd549ddb5f711ee1de786796459f7de8eedf01a8c999c4979dd0f97a94e6693b0f9f1130d0716282a384a6beca988f32221479802e0550c75e91fe730290f909f2c6056df2d95976ccfe592e5e3ec5ed34c73013f835b36b4c07bc39390fb2e52f3fc47c9ba14b717130710b7ad9d5bfd91d0120225b9fb5db37129e16f09097c4ff77a95a8f52accce80f5b57bb09502c8cf1c1663abde4eb168c923d076295d75236741e86f4cabc6eb2bcb0a91718f800365ffca290b41834cbff960d8fd81a2855880bdc201297cec2a75faa18257a6db3b82987b58f1296203d78e19c89cbf8d5d487247ff2154274b131dbb060511837553a245c7ca7e94418c5edfaf6c45ace0f9597218a4ec516ff9a04b5859e09babd84f837b635f4784a796f89318c40adf3833b7328ec4bccc354d9d55506c4e8081c87a98f10ea492bff299961d04a6d42761849601f48407c07c1ca4ea6c4fe8804564e2cc2da04745288728e4ba1a839c169ff2ebe787280e0e967c909bc8c03ec9f0903459ad21169210e60afdcafb436a9bb5f7c7eb1746afd9ca4187111754135d51d398e438109a97191bda76a880818c68b801adba4ee9d45b7c0d16cc2bbbef10348c902edcd5159438a3790d68dbc2a4214108b6d0b04672eaa4e045e4165e60b12887a8bea0ba2f21a4d08f3beea31ee02e01714163fba8386cef3be99feca2dc3655240dcb09796e5da7eed7e4cd1055977c115e9d21dfd95040b89f753e0b85be2dbdd5e1de3c0339fc58474c6baca5f265c5ca92cf9ceac076e32610223280778817f0bc681a2ec903fd17e4146b329c50134412f8f49f54ddaeaee7a598cd08f9450dfac81deb463715a8abbe2297a446bc77dce0b0fb1039f79efc0ddc8acbe2807aa8bd524650f7f9d5cd0c6707ea5edb3ab6e5c3fc103c87172c1e5e16bef0643e73af523ecf12cc70ebcb76c63749c8cace408b74620f5c9c6d1d6807369a89e1cda6ebd8186fc6e0029c28a59d086373e00678dfc456b0c410bb9bff42ae1c6dfab45f691f19395001c47bfee1f3c4a96211ca1b11617ef9d0d3dbe0e773ba412cc42aa21248037406ebc6492d824f99390c36b43bf561d23f0b6bf808ac48c0e0fe93f281c382c180d38f091c98352e26378ec5cd1a82b1762d17a50ee2b15f201ea2abeee659c3cb3fc58d51c177a3adc9d4b438edc2e9e5e9c998b192295a6ca5c1065fb4767ba8d66bb95b5be762eec4673ac0e0ec265e09a57032e4034b15b43a26be53acc429b64e8f42b55fafb88b6424c9ae7aced63e4a7d058903e07f406537a22cc0a951ca90f658756b5e5fc6a972409c6c108808b090487718c19da37c6ff60fe854bebfc0945ba1825c06e8a2c0cf9ad7cd8a3620d26c22e88233d35e0725d2adc228506e29e71f4f3582e1c6e1ec03ab1011c8b77b1575cbe4960d0dc08b7c590c73d3538b25dc0587ab723c7e216208541357d387c058f3e354cf6305ee97a8e6d215caa90ba49fbcb8c836d41d28b404663fabef506708bafb6e3090147a205ce2170814bbeaf0340c2528e2b90c0a91fa505aedf22831b1bd279ef04ba48fa2eb4a8868ebf6c74ed374ea5f31a17b61765d193c2cecc79ddb13971600e5298446d1d633641114db0366fa1842f9af55d26fa8bc1c0c77f23d02ab7095312326b660eae8a433a622da2199948d9e1073e61c558ba5f01eee817e1dae4486f4bf917a1b83ba65db77566e8733a2d32afefed93c71f7715b5484a2ff315f93babce640cfb10b343cb19e1cad5f97b37401d11fb204df467eb76dc04246080e241609d72a08e05f1bb21e89676155bcb432735fc14414522893ce28f9b9197cb938c71d1fe52e0eb8ea45bcea1bd3c27845d3a9eac1709684483859227969c1c839ee918e0bb3f915586a3ac14354b408ab4688491f79b59be2fb96268e719e27bb60700cb76b8bdbd46b28a4ce75b2ac7f5c3b8c1f9f489a791873bd0207c3fa7abdb55dedca43831ae7e99e1dc12d2aafe51e51cd3e98f504bc960bbb075d7831777ee20120f89965cc630a0d80f0b59dcf410dc4ad8f2d0b26d0f83b080d04d049ef18001ef7892742915151b11774a22d809652ba051c119b63a996dce93d0402f02a30c0670b40fe760404cd96c4a7b4cbd710f05cb429280269ce6910c3a6d05802b7819291584e8dba94e4237255991d29e37dfb26d811a54aba3dc2fe22adf1d0b7d8c017600684a29b612209283f48b904eee4df4b4576df521b267b0d6dba3c918d20212a83da454569bbafca8819f8320a60f1a63e6dee47982380a2da0600d85e0436e37dc4187c76d3d1e4cf6231f5284fec560333da6cfc02895954e085d43798cd51850d479d09414ae4f4d43c43c7a221b82e71faebcb9c9759c5945285e6e14f7ba43f06a136edaa15efaef059e0c91c36c6542ece8af3837e7197fec4b8403fa61175cf48fcb8a82f9edc0fb15eed71177afda0cad4383708b38f42bf7f390f93161f6814e52d98abf2a3c2ca5552c8915a8b05b46ff1d04ecd5c9bf264c9b4a37ff2f6c84fedd443da6c7c7eb92c2264ae7d8391fe4fa5bf2013fbd961d196ab495c7ee144901b01635c6360de04db600f7d28dcba2ca01ec3e5384e31c24d3681d4fb284b1c1aa97f10b6f68792f134d7c4850960000e031610462fbfb709a70aae77024a5c608318d4c753078d2d81465be9d01dd2cdacff8ac48200f3fe1de830550045f1310d362a3bca4da51b6b13affc2cd425e9c064ed6e6dd6d314383d0198b76cf8c4e8a412d1ec180c3b719a246ba9a383a31027f2bba2663c02b93731b446208e14351339b225243a3770e0adf8082739699bc43210ec5662c7043421bc8c484518a2005f020b50885a9e1fe33ed783ed74fcc486957535ef099e6531089118419f83041244ad877f8617bb7134ac76f6c1e6cc1feb68183b816853fed027902de71c430264a9e22659fb0d764321d08e27b0c964fe047415890752c1b3534f49955d899ee39ebccb67aaa3a8af5512c727a20e73e943bbf086740ff298072e8464ae57693cd8a216a2b2621bd835b6cfa4f2839c80873e5e951a3c2a4f4fe4fa6bcae62b5b4dc734efd0cff06f481bcb8e595061092d08f255987658173898fede807681f84a2ac8d607dde53e1ea4bfd5e337d7efe1c45a030e51e17a3d73e5b2104284887b56db065e51fe6715ceb424cc861c24ee50ae16091216a1002bf10923f64779e771650ec0c5d77022cedc2f062fd516220716dd296b9ba76e67d5b85449ef42a5cf5ba20ee8b135bd99af69854385341676b1d89d55dc2e5c74cba1187ebe25e52017b1eaa45dacb596ae8c72277b164804b7dc86a85b10fd323cde97c9a8912e69d4602770ee69290724f7b645a5a5c29451a458aa653a8f609bed9995ae4fe69232bc21e9c2a543a7ecefe3815cdb2793edd39343fda8505bc39fbff5742aceb492bfcbf52dfb4ff2bbf927ca50b38efa5af882b7bba76c83c9997d3b19353c373fefafcf7ab5cf4721a6869605fca576dcdb29ab4c76a05c2521960f58b3c44313e109969a44596e757a8c2995874dbaf3f22f72e3c0cd871fea973dfd4f990e8012f44c5c3b0234fc8a84d32496124673426e2b5ed98795b43b577b9b6510e8b5b9b8d93dd641226241c6d82600e01d7d8fe01fc64e9a3334b608488bbf8c2a4a7239e35127f3dda5a9792574a669ffa73a706b5292b2f0c27a17336b0044677328549a74b33aadee672cee3fcb45ae84f2ada1610f3d7329c985afb03c170e0b17a68f1d07c582f05dae2393e9030eb349f0061b29753a039c9077a1c54c20f0f0fbb6560c7bcc82012d789206ba62645cd4855ef6294ad7fe5e18469f2228df149a00a5e60cb7836d32256143884af05f7168ea61121d9fa858c8fa1aac9037ce8e365bcc0029b20f9b42f6ad90bf666e8d11ba99aa83c7ecc2a5c4f0ddd3369eacf2c0550236c1ba170d7e422245514f19a8b1ebf8682fc1e62209bdb27efe6994f6a18ff27990ed420408b0f483d5b1270ab7b641ed662de19eef79befc3ed86f9f872733ebcdc9c0f2ebe316ff1ecf20271080593264476141d53b526e22a2a594c291be3d937d4513247d92ba646623fb28c7932d7f2a52b82abb16cb3002657d617bb035cac8905b454a98cef9390e6639d8645fa6b595463b642b9642e1bdee69746736fb740eeb16378d9db464d462ba1345aff928954c1ef04b7578e5cbef2da90570fd4dfab9ce076b007311bf37fc367fc86ce17494254c1c1c6279c38b86d5ce3ca516923d3dd99d8c2f638b16ca324862fe08dcc44ae3e5de22e11e8aa69e57754c022e28f012f0b5e2d8f1137dac298ad551d0f1dd0b2ba2aa84e611a3240726450fb8232a15a6c91637bca17274d471a1da645b7e5334b38f00e99d83d710988bc4948b6d4638109f1525469791c5524a124b3fa4cf0ba648926ebe4b177f8e6aa5c8a852f5e69f6dbc4f2499f53957d2cb6a23d4f30a36a752111c7545c41a9cf3f6e6f94a0e5337c00f0c0269aa1993d6a6831e26a905678f4229de74a43e3bc6015ba63f845308f6422b9f8f26838140b342183e40e87d17f875cab1f5b1731802edcd2e7a3b26b27ceb11a1239a767880f0b486e455b83a59bc5928cdf42d87589c90f63b160bb7460f330cce2af6e00aa2362b03f26de868318adb405a942df57086973e6620b48ca294e7ce712f98c3f4402f9b7305dda0c1928fba91261574d7cb6f7bb502e3f62bf6b55bb8a7f13e4a8576ab88fa36b87c59fa8e29730bff89f4f563f5d5a444f13f48a28fe1b36983804b02ed14acc9f2b9931ded1299bb47320e8c1d64cd9f15909fd98a5ea7ebbfa38dfe845ad0117cbbc8c2c932ed07f5c416facb2f4cb38f893e58a3f0de4dc0be4ab446be4033d1697a89beb5be983b8d6fb7a7b51c3139d0c2763d5c29a340479b1a9236a3f4b6c526ccc6521d78a6fa07d4b42c8c42ed1b4d58b9c5c84a6badedaeb65282e9506afa24e7d01652dd7d73cfc35b5189c7eafe98b55bfc97cb1224fe830513201a6e562804f609fca4deee154426e50405a86aaa2f00d7c2dcb76993434a4b54265400adc0da9669a85c98206e869248e87cc25931c6deeeda994c9effbfb9c45903bcbc5f75e3a86a45a4c7f425e9d4af374a85e36f3b61231c0d420bf2d04c6a428497bd2912f21e8164ba1ae8dcf58464c4d049392ac7f989ca134b4c8ca3b7555aa9c242220e18422c4db78cf60302ee88cd4a0dd57beee5c0638330808f28926f262af387fc2b088eaa70996b5d941bf80bb44b45ce4b567cd3f81680d7a8f6414e8db07e6133a4bbfdd464b19c3ed84015a63564dd90295d683def6bdc61f49123842e4823d2556644e4e09bfc5b97e9465748324098246482932b27a523dcf3957799df5d71a058bd708d8aae435ea1e45a82b8f80b694a95a41a3f47e62da48acf5208a10b92d7cf9ead0a34265fc4b013c7fee5f6ec70c7ba4bb94ae7dfd8cd07e06c411f81c0ab49728b7bee34d233fcba7a33e7602884997f1167843d45c79ee8ee780e9747b37a2a775f8ccb3e47e1661fc0ac75a404a3aca4eff6bc872986b63f817950dc9a96ce4fe8e34097a2427b4bcbaf7e80f13b11b4f3ae4e88b6e275f6c078f9e90274235bde105f2e91ce16e609e803aae0e8e42482ee1eab244c1c4a1c179773e935b3d712e1c88abd7cdfa5e1bfc7a44856754632fa8085ce91d9e3571ed7a9068fd1b7a0f218d5c602a15605ef918f6d0404fcab28161a51ca5d505899b3669f4ad2a4a2079da3148ebab6c5ad0ba5289dac1b3a0dd1d70ab9db8bf2d69daef3d0cead8b4058eb4d1d1c803f7ee71456de60b07113a27783c36440ab21e0b5b94cbae909ba12259e3638cfdc0644fca1ca0de0bd3f7ab8845a2ece89782e9901b407429fe79fe0615c79373c5fd7ade7c631f110b031fc5c9332fc6af0fee2b7bb56890f4159959463987ce52878b3970a32453430f7bc894affee31dacaad9d7a78cc07dfa5c07709e951812cc75ac6869ce5c4704b6b6aa5964c53c247b315993ed35ed66406dec7eb31db250cbe776e52fb38558925d5b195b9b3cb33930b7f3018a40917203763db6f7ad63aaa2109a21d3b791b4b899684fa1404860d531a886dfa72391e76c40724367319e59f05cbf4a6d78d4b195cc9e1d4274d3c6bd123b98cac064c5817db589368de54c4d7fc07bbbe63b64821a1700f3380b647d2615e771e16fe0014a27864ae02dbc2314380684f12b8731fb5b54ff481946c2d9a924614200910f90b8483cb34b00fe06df70f3fe9f2b92cfc0589248c02ad8f51f8011337b4abfaab246fc91cc358e82c32a1fba965438a45d91e5f7a5f047d5978805d873e171d1c841b34218ec8b06020b929ebbbec1f61bdd2da779484df01fb26b553536dd05ebcab1100d0857885cd3363f4755f910dd2dc192cb489ebc902a85431b55c9b8f18870c706586c077f41d67a5fc30cf19e432ae9cc1ab07211c2f9889e883a860311d2fe701511368772816a8f0d350c610e1400c4f1c4c5786aec72304c810786e81ce0e1de0cadd97e8b3f54538c57827d11b2af32834cb69b894f6cd9429cfb5002a264f7c11a6c48e80df02373b294b1219b1a9827314a9159f0bc8ab85862e89097d1891b214f58267ef01b6c8f35c0d10738c159230be0a78b7c90d8e6b05c645b631cd29781fe7068dfbd00134b3254b18797d4c2c4e9051857d9fb918abbc8398b6710f477b7816cf59be770c233a47c072107e27310358a82eb0c1b0fd1b428e510fbe37a9906b5cec154ebc07a40c07a12f8ab62a5e96241cb4c1f88ff2ea8d37ae1da956f25e7505f8d03778aee2e097e4d7de40247265966414fa6501f0f50c69a027e4c82507aa3450a9df1b2d250def1d0ee9b91e1304cde10633078f414f3e64ac41bd16a48aa61e920daabc2c839126e5a03e5c169cde08ab582a76321bd2f5ca6b74c7ec7ccbfbe2cc9f630bd8ccaeb31648804fd3898a49d1ed07c4870b31127aedab9c375026f2914f9471ccbf0f88f7e4f7c45c0651a8cb0480bd9cabc82d8b9590d1719c36070ae4747c3cbc57f1865677f61c2bc900580ecfdf6ce15002c59af8fd319035d8b44a007ef40720ec25d458c0ef75d1f8dead9515e0759f15716b5781b1b3ca9eecae5fe4442d9abf6d038f564044c84ea9b47968c01fa05836ae525630648c0986ed1d50f2120d46d8fabd683aefaeba65a68b791b6aea7bbc397a4cce34c95f1cc7bbdedcd498ebbcda17a30867c87683b36e712ce0ec7826bce66e28e4a3fcb30d0710e9470e614fae2be19f6ec17c0316e5fa8a28cf995edff4fb64baec9d9f4e9ac3cd570ba187718ef4102dac37ff11414b8bac19a84fb50ca43e52e040c71ec67a82b089f097417451b7de57efd746622e4cb63284cdf39510ad2d888cbc8d5599ae6e8a1d7a1d25c6b1a29c64d7bd9e9f465546279845b4a72aa9641b121d1a271a0855241e50d4badb7ddedd11a7e114b765592c3c740b9b549d852a4eb512ccded1dd65b1a62f6d8f00e0443bed2ef7355c26e91f55dca89d0b09227511c9c43c67663d905bf086c356c930584724318673256d0444295879c84d89f28ec24bee85342746c1e6ed21c60e24656497f531334558516a3cbc0eec8cb7b147c112fc240da2a60808444c7e3d96cde202f2842e48f2a7c5507950db503d92a44e449c4a21fa0196ffbbb99df1990f0821125396f58bc957fa7e2100efe8ed5a088fdfd973d60dd186093d7d4ebe3e64049faf238a8d331134dc5c34ae5757556305c39fab5f979282b04c1499f3bf2325ea2da6c74d8ab18337b32ca464ac7a46f732e723a73f17765e81ad318ff4b3b4934f5c0318200843e4e2290d62dadd8fba5cb7c7cc139e6b806ce3fb0d15048db9781f8431ac2006b0c43942321d2bf9195e021b76587551683bded0baab9966e1e3a23293a8cbaf00c77f666fbbbb414ef234e711259f1db83f0f5970750bbb66b33ae505c1f95bfe1e69f3a38712e61a0e0f879649a0b52c88bb8ee3f537ebf31b058bd161437e57c94eb5c089fd96d5a89a0e69922f891eaceea6edab8e7c1009f945446f6284849019e411b94a8f2f6c325a448407c58d69c69c02b39d156ce67cad5f478bb0f747eea6179989d552ae972c32fd7b7fe133d0d788fc044931f5b2029f2409e9407631244388b885b4fe81f97098b37c14f5e4e1db0299314efd2cc58ade28a8e280aaa8094611360df81914217c8357f03a8cafb9e90d427b138210e73d6bd0f248d3f23e4d6185e13096082159bba9a935c17b4585f9ef5b125dff8a57e353ba83204dedaeff55d2dfa7702d68349032bfb9f1f7cc8e36b3cb486c4efde6e6286ea686bd1fba0fa0dc747b1bf0e1c992d7048effb557e0157b28af8ba920f462e25767d984474d5a2b62f5376363d40fec8329c442dba87fe21868999677ad6af85c002d8a9ff78c6a064d425514e5f62032e8f223c9897a6a7f0aa9bd115d8c5002012359f25f831baab4eca06748e5cc1241ce99c6d6b233d9a2b278dacb5a041bb5e379e47392da44fb1e2b45e4b930d968abeac38783b39bf2bb27a422888ebb778d1dcec9912c71f9701692722baf4a611ca24e069c2ab30e7ad1026b515ba3c7df8905044da273c07a661e949e6db95f21ba355050009d2adc6d5761f18d806566f49f4de5558c85204f6d2622f7340b79bc155f1eb407b05cb95efaeb3ea028e3aae015a9e067e941fa80cf135cdc28ce3a544be6782c21dc134f15b1623231eb7876a70f52e71c03120c67555410e2b2867ed09e7a5d4e4476ab76255fa79618eab6f112b9be95c5c9b39ee38f6413eede1b7e5ae3cd8ecd1598697510526551c76345e637a08dea75a7f5c001cd802adf3ac57f4ec012218184581319a7e85cdafd602555c339a6dc639f1454c9cba8f3425e70d6264cd31282010599c0a20aa2e37f4d16ba02c209dcc58727f6a74bf7d493ddd5a0349c1808b52daa39c9f5e65a04d8543bbb3c85dcea17982e741aee1bd9a7bae7c111e490ac37a8ff2b9221aebf7a4acc87120897d69faaac2e9333bb13fe7a436475cd0a41170d52dbbfb83fcd5c9857039b8d4605b372c59ffb01f8b95f3a1fa85bffe1a8b460bce6cf4a2b1e74feafc5d21e1baf61f0f1880f4edcb08b198f43f756950469cdff627db6c9147158a4d89a270d9788bc79debcb70a963bbc22cb4c32146a2126d34f48952e26a7c4646edeb11737a3d2cc1493ae6e83089c79b41ee35a8cf9e5cf485827d18473fbc566cf73c050690f05bf25439726a86b73c9bcb481bb8d9fef0ff78cefb1ff6a0b55dedba125c0ffaac43d9301ce07810015058fe46946a203e0506983f299fe7561769ce5eec80afe624fde66ec2928427a3b508936de1addae73b3e2232aa6d3eccc5b65d75eb3e2b391b4b720c2699c59e7200dcff3642a92d2bb737204ac7941defd91cf56702494a4e0bb41d4445b554a96764836b79016462011eea2253139702ee70714482616a6ee14c8bb5f58e21d225095d968a39256606cd66b860060ef471b496bc56ef997b85288ffaed7c61900b9d8d220fc3377434fa9920d2a855526e2afa92eb85fdf78892eb976d145decbc59b011987264a51273041cda9d9023b22745ccd34a600179b222e22d5e9fcd3f7e4940b20f56e7bc4a15658db069ecc6cc09ff4316b56e01280384ffc260f661849cd15bc2e43240e26f63dc8a61ae186a950cd0b5191b2ab1c10b36f93145c022c5fc3c8b0aa51dd6e2911837bcd4554159e3c83c9b8fdd60a9da70987a9bf3388e6a531d51821bcd66f5df8e5a331c80772002d64a2eff042f82481e25c91a48bf1d6c0f6207a25410f844a9e12f36d625e9ac3e015d609e74e5e9036ef41a66b2d4b5da49602c58ca359600c8b75b49c92915500df61b5bea557afc4019c11a6308133533c7e77d0e7db7ede19ff38dd50223ee977b69d001a641116c8050902fa558809169acb1608a4fada9949db8fb6c5ec09aa183157c2658e30a49d6ff670a50d378ca01acf5c2d9470f8ebdd6d3e8ba0c7309b98776ff9561ae28c9948e77ec6e90bb92b9216acd22e38f1755663d88bdbf3e653258656b7cc4b79c1355d3f5ade82ca2938bac8627d81329c91b7e05e72e8dfea43b57aaee4882bec1e62b8a5b56cbc65a283ddd5b09e67aeadf1e584ec6fe6f420d845a4fba72d7e2215551ac84b3849f03967cc1bd14b7aab55f2b82958457f3491b17cb0e68b4ecedc2fb7a1765d540551d13ad91c57adb267777cefdd1af3e8f29cfe94459372384fb3380d3b9910fbdfea4b771ee8e6060f500a20997b904cd68827868a29e33ff4bd84dc6318bb8fa0ab844070e577bc9af81694f0ccb7b44b3e40d0ff6df5bcb53e038debbb79bb920e1e2f7d4cf6cfad1793c9ea2ccfaded8ce80da13f99b8e2883028a840df08595442dfb3e3c0ec9f1021638b00c0cb2487e6ef3c18b51fe1ab28a0aa400b0add28c8744f5a34cda947bb1ae26309aab08947b9185894155aba9a9388858d38114df15fd66f48114f04134316e60a27c3c908ea3fa60cd386212f41ce1b1809808b00627dfe9490e9f168a62ad44dd9ad1518dd866ecfc89d3be0a0222f6a669dcf36d63c0439d752032ca13672d773cd7f41106125580a8ede918eb21a1dd15ee878a33f79980ceab4006e00d0b42e141a0104c118016a7de90ac10df954ff40a60cc65e211314bd11bd1d788f09184007a2f979e24d674762bbe2affa53a4c1d8c6a4c1578c4b2402bf0a422e2d45c9219b93c67b752c5a6f4821fbc5efcc13e2c1c7171406704f62ae547ab3940988fc69839d8dbbb59a939d89b146b1878705005bb685902412bc7630f6d88db7579aa2bed7629bd6c39c9efd74e1bfdc5c0f8016f3abf982d67afb72104bc280ef5cbdd16407908042ca3e5aecf531a012cf032242081c543f493c2e888477079dd3fa3e105c4b9d765183f410d471b92afc70e64cc850b6432793051b40a513827e6a010a1f4ef56000e6b43f43aa660616c904053a04efcc062e1f6f55fd1c38509c238dd1514206478227d077f01ecb46a004aadfd79d8bef133ee8b5c713f262fa546316bd981cd69de2b75dfd3b754ce3130d58b7566950600e0185d339ff0243d5c82bd393ca096b30bd451f544a3ce98087ab61feb0fe99fb4dd9274ec59ebc92de760c9250533c97ca0764bc0e1fd47fd13d8e8ae6541945a3059bfc3216f73b553bf4632df5d38426d2ee21a6fed78f7c518281f6804d7d9d78a44e06ddebec1a27b9875ce5e5c694eafd4d92553a00522d01f3d014cdff52599d049c53e021434020a091e7256208a526e0e99fc332019a40ffecc08eed5212f9f5caf0e864802f6b367adc680d45530afa95ff00f7213244dd4fedea29735fcf1bc80be431c6ee06359fdef1f6de5abd0c9bd625bd98e2ae7e98aa3979696966c29530407cc07bd06378932390fc308cfa343ae830c706da0c815004766882949727572624491c34990859a1c0374e068c88db8207c8d106e1526f7e44d07ee927b04cb1c1c554e4d6e0144b8c91100f7c25d2898c8391b28aa1c02705bdc277f628b9e1c6e157e759988e363e4ee1730bccd85e267dcf0592207430e090dfae426c06d20a744aee84c89a3e0f670c4009c4f550438772786b8c34709104e94e440e78e91075698a2e42a8b1773e05c59dd3e37cbfdc10b2a36e863e68ed4132839578cbe39fafb961d2cf4571a7ded13304f3ec8f1e2c20f570c1cd709f10152725ca8d143a60731e4e499e175d0d4d064ea861ae6b401dd0a1e46d6f78d3e862cff5efce20c11c291b2a385866b267e9fab05a43d0fa8bb3326d7441f3d390fc78599a9dc1a19b84b850348727b9c0a3a570acee9ecb96cd0c029ebe04a3add8bba9c81e4ef7ef3845de7799ee7795fd7b966ed77aff0eb5ae90002ce91ee007f0cbdf2bf153ed275c116100e454d732b1d7a2d1d82ae7189fcfdece5fdadba735cf7cdf7a674104b065fd4dd97b6b04b9e7979b44cb9ca73e55145751e656647d46b87a765249367de1426ba7b0f0bce83abf0ab7b3c07bf70dd5567e53d7991f7f7032f08766ca2cc8c99755330951046835f6b1c47f7a2103633a6d381ad74e974e0249d8d295da2b4ec98f27af2dd774f38c715ce31e2ba247a1493c3ef92e82ec46fbe63827360176e835fdd77df3dc139461766835f18d74de9a61cd1dd7756380708d58126e8e8ee4b1df2ccada67455d21d9a29fcf1bbbf91bbd7c08bcaef3a2ce3c30f47a1f0658862f8a0d0c8846705e748dd4a33616f4a159c63fcee4bec43f40074d17c14c23f8ec93284bcd4adf48f6b5c228f3ff384f882738c2e2d3c785d700ed075e7dcaa4b75d75c366d1c479e6b75af2e95be9865855f7930c61813691e9dd5bd5a4335e7cd5826cfee1b2c63b6ea0fc7ba33de10f1d24a5f7fb75d3c25c66bf40dbb230fbd68f6ca1dc6186949b9a365f32290c729a38142441cc79d3d51670393203800488b478ff1c367099df8953fb86c581f0a8163904e93e3b34a21da0e4dd246978ce8856dccf8b1891c868fc2117e5d0a601b178810080e88f2bee83c2f0c3f9a0cbfbc2ff4c047e108bfbcffbceeb1d841750f8a1d49de1b86611886cf0313899e8ef2c3ff11f23ef46c7836bc0ecc23eeae943e34bb6c527ff38561188661e86fc671fcef297822873f732bd2351bc7ff5efcee78b19df2de7bef05efbdf7de7befbde0fd527636fcfa64f8f5b9bbcb684740fca9d0f89823fcfa5000d286da7b2c24d44cf8af788558dfed80c0b995169dc8b38bd314e056f88e6543be382832f922cb863cceb2169fb4504ed0cafe0e02604882562b82a35214813428dd451a3ea248a9aaa2af1e89c8adbb46df5b25c5835439c1f8de7befbd33c0291964a24a86051a7f34f2775ecec0fed1a8f1fdbda3af9eebafaec35440717f78bafb1990aef1d5f85cebd55d96fb6e1890bf3a5a8cbe1b5779d6c2d75fad1ae07a83700ecf95e57690e48deee37fe10b3466b158f88583f00b8f5af27f42ebd59e97e53e0fdc81ddbd3f45bb149d367a36433b9d3c3ac8eda3af0b82ec3cca07f213972ec8eda3854a1ad9bd32e7272eb8557a8dfb598eb260d78c9c817b759432b27be50844fe1983c82c0fae2e3399dd0b4721f2989fb8742fbe9920fad3f97229c5fcc445c41be0cf71abe070e785d05d3a25c806e708435628f4e38543c0c0a7bbff8b2fb8ae11bc1e897f44087f58e0f5ce0d8b855f9d963c63b132502b08cc2c949f979fb8b80ceff3fefbbe4f1473c6ae2ed7005da2ce7a268e54b2a7c50aba74308de7fd0afab62610c76f46a7af2b040f1e3f16018760019766f21e7718cb70c731fbf1d1b9b20b0a1649dd913e7b3d2349b7d1bdd7b9d22e55c7083dbba1bb876997aa5346fbcf0758938ea3cbf8355ac9b31bddf7254e7b42ebd518bbb280cf035f2157220b29fa03b54bd5d1a241d735a2f4e06acfdd3f71b911740f82f9f3be10ba07bdef5d871ffec0230ef96228a50f7d1e166a3508e4799e9783d04c1c73c076becefb843c31d86cd1a3841b2f3818c5f0c506234a7628c2c6a81be349b9f85bf8ff058d7fe845b403b75ed42e4587c916a30c5ed7e1cfd6fd904e8328a8581e8f7089729cd2bdd875497861765c1c90762939793c1da4e65c49cd4962e7053051543d79228b37461e114e30f9a14e982aa2e618794b68ef5b30d091417b0330f2c6296f344bbb949c35188c58817bc5e83ba5ef05d3aae187382c447921cb9d2780c05161c41548204982a5a4e49411c110f5e4a4ba98a3072715876a8e549c373080e143c4020a0ab54bc581220e989c1421a4e23c49c5e1221507cb7ba393677d19bf8066accf053442e559102b03799e1525f85fe720119cee843e386cf4071e4ebb0b4e9bef809eb8cc5851da7f7e8846b4e7f2bc305d5cf5a067a276c344bb1b1ddc5461c5917a5346bb949b2f526ea4462858881b2c393a0c87ea60282d46d248fed13e0525c5482793daa7a0c8e8598b035062a080c8601d5184da527e5032d0f0c62a28293e060a6a5cd287b564cff7e3026b083ce1d412372cd61229bcf01d2058059e3002d0000cc133180459af21a0cc6a3d110b28a8d43eb5a4093d1b8aba57ace44ffbd492293dcb650790d0e49a2541b48072d0108fdc7a52fbd4122e5a86f6a92535e8d92b0ce76247f86288a7081ea830a413865c8c5e00f5f0281e2d2b825ae1876228d1442896744431ebf05b59eef8a0c3300ce964ed534b8a886544708f88c1f1c33154f2c5a7c48b506cb5fe4543fb94122bf48ce86363263b38d6c84140b9d5461445b11c620a3df3a8f18b1c6a9f520245cead6f699f52128436d3bd295dac918380722b03056940ee518205828ca40b1650ce4a5ca8e11b5b435d287e1806e1571886534a94e83074d1867418927a201831188725b6ca2432a87d2a491bcda3d301bace804ad0f88810bda0568b0707beae8747f168f900625204c3508b0e431d86651d563895048b1789411e05fe0d10784c2579d20f689f4272a70c63fc6a1c915f96415e8e03bf7bcbbb2022a4ffd0d0f84343438f5f415a74cd444c6369f08140a156556669cf73b54057febcbba1ecfede7bef05fabeaf736f8917c5c144294131d9f1cb1dc82bbb7851008a99b13407f78b292466583e848328af766f0a8995dbc170ffb29ce47befbdf7de7befbdf7deaba5e462831dde40e1828f9a303c1885be040d71e680a1414b14401885797468274a87a11d291dce300a4b3aa0f629244e3ceff415122fc642d763dddb798ee7b84b2c4521f03bfc75da854416f8422ca1fc5ddcf927a3fb30bc1e665d7625785b3a48639f3ab247e31f0f3eb8feba7ffde55ffacfa73f2196761708031b3703c404d7d1e386f6a4d4b819148070b0c3cd1c51a0038202ce01d40ad2a0a57dea8816bad33e75040b6d640aa0324e202155421591e487510a1db6b0523aa1013d479cc0708405ab2bb20378c839714a1eb2288f50810105998498f1ce11639a2f3a6cd1e186286228a160e1490f2c2021958c21c617e3167348315bc85045f944074822c68c689228a100415671c98365644385ccc105294f4a4a2a9e70641630463b5e48e1829462cd9333e3184d2801868b1025113e90567220ad9861f4424ad98411a830468ab809c385aaf18d1b12c9153929ca1aca94478421bb1041e236505269298ba8817c0206168a3419f7147922c3a7ace20e16384ab0217fa082fcf24479c3d4688511a4132fe5921d463c3690b92925935c0b53e48ef011a3f492678c2347035a9067d2904c9829c52c31e641e2050e20c103b9c395196448034588295c92f24a9f72093d40d021c768436aa166bc228ab20a13e41751608230d30379040719aa8c5451ca267d9650d127076545fa50876cd3c5182b4a3268ca2bc8903328c1c310250c5da2b28c6b68d8f2829a319890318c61a464614f19c60ed9c21b23b018bb98e2891325165264942246ab1f46353a94b1426e40036413319c8c5c88c142d9e58a6c020f4915a7481667a8d04051da20468a23c629be9453b6906e6e20a1c4304671f5a474e10ac99531c0f882cc228f73482679dcc20835650b674a2a64481ac2906114516ec0cb28678b972be41054de4429b5f081aac2c7c858051f120b3c75e48c67b658a2863ce28c1d32a31661a68a98e285ac620be9c49512072a5fa29035ec812aa3ec315282e12307cf98851cf26a8b3b6ab69c3143a6a4218c10459063bc90386c297db8e284cad8274a19061489270c2341f029a5f0c491f3b4054945cd1267b2908113a6fca108d20b2f639d2de5d315920e152ea2941ab082b2c1ca0829c587bc01cf18869c12882dc83e6a483e67622053e2c224298254e305cb9612c99592092ae35594226040953b80618444c3872c02cfa8474e99c31656a821d99c79814cc9254c0f458c587829956c29a3b8328a91452a649a2c462139a0874939a5878572cd18e41560905aaac63670462ab47828d468b28b1c6a97fa028dd6525f9471b1d8e07052ce191af23ca02ba0728e17893eae78415616b328e67b2ff87d9d57c6c93351eb081f7f50e5f7df9e2e04b37f5348a468ffbaae1bb5e7c275343879c6d2dd1e201d4408701d432c7df1a7007dbfec22cf88761005611bf7ed4cb1c382f69f0f5a6d86bcbf4fb423cf101a7fd07b91e7d280a84e9e116922ecda414e91bf2f70b2d7a58cca9345cf86c63999d42e95670622bc03e7f01adaa5f214d1a476293c567ad6f2a2ac1a42c5f0e0430b575de8d172e749111bb8483326870787e3947bc4c831c94180bb2cdc22ee0d60d2f8e05e7895db61e1ae0d1a80a385d5184d72657235704e723568c9893970456e14be84175e1ca47c8ba89c99fbd17901cc1850a4a802e7ce9d171e5843851eab2c90e0f0248b0db9040ce1c215b07891d30354146ea4a2b063270eb965882aee2001650489219ac871c95d80bb22f7458e8319b87c27a7444b4e2e0d063820275301ee5a01a24e0ea709234914397cec50855185c74b7e0147e7ca161cbcc8b3e54e79d4952b8367715f10f2393694d0c0dd29c6f81b2c2e1a2d720ac045e992bbf292fbb1200867e71ab9811b9bd2941983c3153e441c51074f170fd48ee0dac4e043981de4f81d31b917006e86db8554ce85a3c28ec5fdc2bb002386dcb56207cec3742077851439d80170787262721b10e1aac8c9c95d03e05a08a34a8e8b07703e05ee092ff0b4f040009cc891383c39173cce10a1143900e0768873f338188c0bad3ac2c3d8df3533b5d5cc217b5595fe46bb954cf69d523366d26cbf99ccb635652c8bca8ebee883117e8c50828e006cddad493b374ab3d96e6cec8f1e66beaadd7d02cf5f15778791e0f93380bb8fe0d95be34d7177d86f93768bd1ccd83bd1766baeb758cc7cb5fda51415a2d6f6ba9b7e08aab4ee33bd3debb499926eec2ba9cfdefed5f65b552dc189b6d7df11780318a0c8c711092318c000b076355bd66933b1b5dda2bf97cc556dd9a7d1cca7d1d010dc5df4eca5ee7ee4d9a3c003cf1d1a77a1a514c5d1aae99b7f6e25f4d3dbfaad9ab2ad3a022cdd42d025f559a7d4fca51455cf6f6faaac459b7e9f6aecdb6d82bbb73c776c0fcfdd05ee7e82679ca6c833aec1dd4bf08c458080e76be5b07337a9349af97b359f7d1a4dc368b773cb62ecabedb7b6da6e5bf357f599622a2de977cd64d1a69dfe96b54e349bcd7c275a1bdb2b7abe9359fbf5b6842aa56aac01ed36212501e9d902a4ea2fa5e8996edbee013b376bee3596a2cf9ab23335ffdc4ba9298ba5b76753d4aced75d7ccf463dbc6ae3736dd4fa3a14d4fa399b1675159fb37fecd3ff78ade82ecf553156d4da51fb0735f005d55597b333fdd3be95953f6ad296b7f9fe6ba64bb7d6afeaa2a3dd23ed518d2db705acd144dfad7ef25b386eed5f65bd63e10777779be46f875770a3cdf1bdc614829ba84ae2ad23bad5bc87ea7a43f7f277d6a3600c6b49ab5bd7e8a9ab2188e54fd5d53d9df4ca60832d60481a6aa494cb12d8b75706453dbcd9a32209b3565361f6dba856cb64d371a4395946e2cbbee1315a2ae36b3357b98ad79ee58aaa66d7a531ba0b44b60d75dbbb1a91a33571f3b96aa2314151dc9585366da766bb66c8f96dd4be8d9de6ced8d84a222196b8230c2ba936ab7b4031f1cc4f6096c0c6553746d876c10d67d9a5b765620030474b0e3c6416c9f5001a20c5e013b6e1318b2cff487bb07c0dd7f70f724cfee7eee74096559f35b357d596b026157d849436f493960186000cb0087aa94a23836cd8cb11728614044b4010666525206199c3a3050ca81434561ec6ecd35061bb2cf14bd20b6b21738ad2d8b2eedf50275693563b00b96d25beb74935dc0ae9b85c0051f142d4a8fc2e1b4779f9422650d576e17fdb5f2e846acee9f587fe48dba1f859ef02779a3ce7bfc2c219c87f37e743d2579a322de630fab30fe7d961091158c58e3df57a17c225c8ebabf2e22f7bb27523e7e098c7fbf7411c99d8d2cd98d58ef46ddcff2f8ff3fba888858425c42efafe7aa11be0a35429704bcbfef461e10968bc835eabcc746f8cb22dde3babf3eba0722c275a9c0fafb4ed8a5029723960b49856fc47536be27323eeeab425710efa399c40fdf8d58ae925cc1884879bdc7deab40bee722e2fd2844c44584f5a50a3f2a70095f05f231f9f88f4857e83afa71f1e86c7cac2cd9637dc912c291cff2018474016159c9a1f3c0acd0c514fefd9e475712b946d70adee07b96952cb2d1de771f53e85830408410df77c20fbabcee9dbc1ffa3c2c56951ea4739af85424871f7b451c28fd5e26f25d83413a3d7a1e0e452f24bd6e0c433274cd3a527431759ee7795d5762cff3baffdaf0bfbfe07f427e9f88fec4efaef898145d33f13b261753f8e0c7a3fbf05f26ef89648fe9be2751fabdb7fbee1b7f786b2b04bf2714a4a599c0774dba821009d2d2a0abedf49d797f5f740521a24317d3ec82a0678d95c837b07460fade7bec752b7437049c0427f91eff539223f888106b86fc94844b1006e8fb47bec77fe47371f1e151bac34029763a887039127f7c22449e92bc117814e2ef3e03f7bd273fd0452403e3831fba321092422c1a72064697e8ca40e8323203741999d1e58b2f0c254b4bc65f8a8e2577451f6b0b079d8efb9e5954c41d563b4451b4e2b3e24be39150e8195147945ff8644c0b684a220d04d9117545a06c11a178d1dd0f755ef75d953002ed53506080f224aa744fc1cc8241ec9274df3de9be9b82735c9c9529dd7d8705e7f8d1dd775aa2d0dd773c745f42edb9427d1f9bc154980d7ef383ee1ecfc13920a8ca730582fa0a95e1a3a44d12305ff018758fad743a7484b961082ca620c94109a32ea7bbc75db2e84e882d46601da1d4f7b179536545e50ae3f01366e269e9fe761122aa8516a4700ed69832683a5b101f1a532d30e9ca3be30feb8572cd38e5848b9f26503401a367af3137e564aa499a0ef43ed07bd17322d60b9fe745c5313305a7e7cb0f675690c244ea0a2d7468e2e68a11c67da77daac9115ace569fa02456c852858a0c513838c112c5a7a08839020f0b3120a91ac3025de783d5244a932e3f4daefc48a07daa491339665a4d8e84631886f123c2c9e1152548057e4ccab03c2651acae63f285c5440b93299989133a3c7a30c1c9f809c32fca2a2de9d76dfb1d4bd5fde7665285a4e8efa45d435bb53581bdb566cba6d0b4d314f65e956aeaaa74d2d4b7a1b637083cff9373db60acd9f44060ff0eb3800a6663555121a66c27b168cd022160e76e329b4cb000c93d699f2178b64044cfbdc6fe0612d23b257dd35100dab8bb07804a0040b039b5676bc6d033456f4babf93433453d80a56a6cb36adbb22baaf481e70aac2a3093de7678aee009ccc6a9b6afcfe1b90220ee301b2c6c09b65f7fee0a2c0832e3ee30965569dfca96d0b5bda99f2ea5e88d1e3eda5b5213abca82acb4295a538380b4e8eb6bea6a636f6cd04b5537ab6b54a5e89f7b5559b3a9a69a5043087798ae817ba41f34b3558f907ecb8e6aa0a0aefb02664bdb35f2eda4608c3b0ca98393a682505474c48112096d6aa2ab79c249536508f04c41126785f0f04c010f21681b33f7f94ea7b9575a8bfe8ea5287baa35354dcdd8961d5120bac3904a3877cd4c09e079022a77189207434c1190fedcecbec0deabd2081368912da1b257d596dd27b8bb003c4f3004c75e6d4eab89c650a59d6ea33c011077d8abafaaedbb7b069e27c8ee34b3c95c4f165d5b76afefd4a22d36eeee4ddfa6bbe664da2ee8d1a6bb96aa68071cc4f609aaac69df68a69096dd6707471c289160bb31a9aa038a8a8ef65e955268da69cb0120a6e519003d000005d8a9cada5d1300add6ca60eda6d14c20b036765b5bf68bdad46cfa9beaf4bb86b60948009379a30213307fd74c1f9e25a842821e24984950c1069e2378e3b05565d1750b41776caf7fa6487baf1e20b1486a6abeda16c073041e54b9bb099e816600fac0dd5f9e812c20f20c419dec19022cdcfd019e217002818e07b0784089f6fc0008ee0eb3b16f6bfb7adbde4cbfdbd45ccddbd2528aca5a1b2ff666c6fe066bca4eda4d95b15bd6a6686ddded3ecd4d53d5a4d75f8403d6a61bb5a1b1bdb6ec3e911e070c068bb5fb6fb037d8753ba5a6539269dbad6c09adfd4d75ba41a3a14c6faa47bfeea41b3690f66d6d915ecfaeeadff81be7668aa9b4774a4d3635770dc6a6b06949660a0f5b372dc9fcbda26dba8f1ee80ee0990695c3905a555d919e4565474aea6ad26e4b7bade19906961b9e690c71d811092494b063abaaf44e49bf6ef6f6660a01771f3dd3f041038479ce54ee3e5e003024b3a5ed13e9b710f4cd679dcc269ae994444b5527b3e997d2d664dffc143db5bbd3f09cbf640ab83bccc9acc5d69bca7eabaa6bed5bdb8e0dd97f0349d69afb4ca169dd4a48cf9a437cb83b089e675431e3c88c1eeefec3b30c3e328c7087a5706ea7a41bcba24f335bb5fd1dbb9d7b7da474bd2521ddd8b424d44cd1cd64d676ed653154d6f4375876afe7697e0ae97a4b4ae1d933bd29b1bf99505a0a819d7463b33453e959f491b6109466a660db9b09295d6f492dedc6eef5cd67cddfe7d34cb44d77cba24376934adb496fbb3d7ba6fbd90224fddeabd29fe82d084afb73a34a68bade8c646c1911fc44fd54f959e127e6301be97a93c55e497da716a5a14928cbaab6269546fb14fd96bdcd7e0b3165dfaa69bb69bf23f0fa73eff4d9011061f0ed2da965cd4dfbd67c25f4f7567a21a8d95484a4ca9a901e070cc904a4df49dfb23711be0807ecdc1028814d55d6dcebba5125b44df7f98329a63a60ef55e9dc42d6cdee955d32691e986b3bc407bb64d294d27d017555a299dba63aad6a6b9ea9593b3a7f9ce88ea526cd346ad39b5a3355a5f487aa94a2abbaae3e7aaceabaee1464ac292b2a3a5af7996e9665339b340b88cddc6bca22f220cf2c31dd369b29fb9404771ff25c5239d168372596ddb1d88dfd73b79bf6a92ebbec35f6acd9d4b4d3520aecdcad79da6ecf9a4db122a492872cd69ab5ad9e4aea003c97d9cfddb6682de9268167b2ca1d76aa23904ccebdb2e752aa3e8bfe125ac381b6b45bedb5bb03792665ee0ec433d94af7bade62bba9dd49bfcf57db67d118aa841a791e8b5080e711e7ee30a5473aa1042435b6cf5fd54742faa5145db7ac457dc0da98b9b643f6abed9f9b869a1eb8bb089ec7d7d27ab3fd528aeea4dfb174af5b69afafaefb02385216fdbd6ae0597c736e59ac356d2c7afe4e7ab55d6dbfee96862a6dd991bb33c0b348c56ccdf3778bb2aaada6c6bea5dd4ca57de248d5d4dcb27da231f466420608b839ed103a70da4c1ae4b875b06f34f304a71d0207b17dc23ed598bbdbf02cbe7f8bd6b657e039a4aaa9ab52d8c5614a6a079e43993bcc666ed9b30568d35d4bd5151592ee77a225d536cbeaa41d01762f99360b3c8721079e4132e0cddd23e0190c00e8196cb93b0a9ebf2877187b043bcdddb229fa482920fd6621f0b0cdde9e66d656353df799ee5a4d8ded359216a8c42a98b06a18448a2912810c0000003316000020140c8905c3d12047c258f21d1400086dc03c5838118a678220c7300cc4300c022008800000000010820008c418431adf00f1132f97821cddb1b6c9d055e3138c4f244b021a92b74fdc7d247fc7045c98c1928062ba87e31a60e135ac952acc9e6947451f5a3769ddeaa685a7c1a437336c708281ce24037c91651f6133d237bc470c85b7a8c2743ac50682394eb77fef803bfbebe8c95425fcb690d83a4e70d6b28cb7a1f54b41987768f827ae1b1427825fc9ed0d557ba1eaabbb1cc4f915ce5f86769df48e81db81de511f6f4d5788adf53cddadaba6cadfca5c4d7589417c640f96e725db33506ff4851a66d82a15a82f1a9803cf0f64a7961f7c43c9b1664bb932f32aef3c448ba3274fc92a49186a35b07b3b10e3af27ed3a3420afaef47a88edee11da2c79bcefa13ae8506d7f4ccfad36e8d897b675ec5bb38996b6546965b579b613c2868c67b2dce4816a599bf8787ff760c82498080af2c359906a7ade5b2937230e08a477b8e7d87cd633db85419b9b1760d35808502e00391d8c938ecb7eaca83e95fe74381eef2029dec3c51c2661831c431d4808a742da6cf01642b2f3917bda41ec5d05d3bb2a6fccd878766de3791f02c7749b8f6d9d52dea13a5dc7b38439a25ba52c6eee2165e5fdff1a35d7607f9ff804caed527000f62291c03a92f925ad46d3600ed87256b45a763887a36b52627edd6c49e7943eec70c11b1c89cda9739229f12de4e87bac0242ead50e21d1b1e4429741f06cb77fee7429ac72c98a3fe6ec9de819b466725b94d1751f208a25007fcea655379e83a9d62609ac0aede6036afb723a8de72e02773e4b5ffe1347dfd945808a0394e0cd0f95671b7e3846471756459d1d452df0d247d9343e35a2ea936022966d92f1d605c22de913e881e0ce0e56ed765ad9ad873ae7524331dcfc1d5dac7df7e4dab413ee608aab3fd170a4d9363e0f672f90bb1d825366a38b5612054d3e024e975bbf17c72eef901523f92af72fbc121fcd5431a6a14b787158770972ca510cd379c76b231bca9420275ce4331a2d3e1c0cee4f829124fc144d198250a4c9cbf82a93113fae307889851b35f34d2a348800c7cd32a2d48b03db7ad82a7cbf50d35038311144f1467fc4b1f76a0e350a47ad8ffac4e6a1a2048f45989e9ef633c3bbcbb1f417ee5baa1705defd4c3329992323247dda967e4c692921fcfe59bc418d847f391114a1e15bbdbba817fb22b029a86f67100e48677229661d800c719f388c1d121b28d02f519f543e139997fa1a965227d5768d5ed72ee3b5d0bbe85fcab65def5575141ebfc6c0f218f3451703e59d8593008b7cd02dac310f123a8258cbff90ca1a73a07013caf295615be5e953e4ec3f8a245387f755cbb90480d59103da90460e71443bd7a603b3ee4b67d42089f58ca504371a60a366e32ab08d56d92547884eb88dc0224d7f4d9f932f3db07a6af9781abd99e2453e276985a3cbe1b758ca628237b8b296277ef926c09d99a4e225d757a690a045d2f2f72407cb5a9a73e4fbdaf0065c98f0195bdf1434726c3433ff9b8abe894d6b6309c0df156dea0115aefc5dc71742cc9eac9921a8df6358935f627b29579f8c79eacaf25e87d88998bbc3eaa3ce44f157427980a4473f72089d3f203f977d56a1a9aaef691a9bec85a697575b3abaa762cdace3bfbaed834a49f2ba143d418a0497394040545c95683de5bf3f2749af88b0f79d80edc6406bb3fc69704a1df6f6cf42aca1b2ade49cc7108e699c620b200e6cc45f2512b96f8151babde2c4d42356c5dad184f657c701c2b0adb64a45f73272f7a4ff3fd1c702850bd6b0f9d11d5b90ab19012f6a69e7c3c897e810cc59f0b14f488200af0cf75f33df642023946ceb189708d59d6a7837b7c279e6dd5d9ae68089f9cacddb4092d6a9bdac628973cde29ffd8f09f7e3726d4a7efe75318d0573e2497edb1fe605939f02b94755fc3dbc75870c3f683eb1a757c9f6dc563f14af7e2b38c0cab918805bdfa6780d9b96154e889eb7913378408dfa35d14b993f5fad0cf47a51a5661180b83bb2fdd246d4f4ed9abcdae999e64230b7e11672f6820325ff2ee763b6b91b0e0f19cea1e69c8f5a3c1524c99c080ae1b76d7c419417917330c1958252f172c20fbb384501a12c3f658ed0d0ecbde56143ef5a9ac507d7540bf7469a2f91d63a867e6a9673b58c404a6ea6586ddeb0c21ec11b17b8bd34283259402a1a82289f23850a0f2d20e9978c81f732bd651ad3c3d7f9655ad66dbed573e91ad4d3f3a237d9929910fc3eb601b6a511c3f59c1c005b06be4b2c2aaad55023605f638fbcfc9553aea87e2db532d93dd5c3cf028bb9a72cc2883f3aa240f72eedcbd3bc686b34caa9eea78f058b2d23bd35839e57a927ed6f21e405607ff20247c9d1e9dbd573bd256b7b650fdeecbfb94ffab0ceb67849d89030d7af32e33ebbe9f45c55aa814ec4be5fe13df74d957ef810c69afa4b463d3a09be97557a9178df4ecf958fe1b2e0a2bc4d0d989c4b8f5e2c4f5e36be426ecfcd1e9c74f1df8148f816ffcec8014a52f4575bf4baf093ba8b952ea9b1a602ad9dbb1d0c7db8d0911e2144b06f67e453837739f4e03dd93a46cb78b1075c882a0a664f5666cc6fc31a6dab8d454fbe6190d2a14c83fc3feb34551b3b22ac416edd88ce19511c5cd0c397ebcf0def5ad643cf25fb760cf9ed64c1d4ceed0a41412d9efe71459a5ae8dbd33f77d4a5d7dbae59865773ec9a607f4fdf57a5b1b282b0041e4b59b27dbf101d3e5830fe414b968bfcdaf95da9f51aefcbdb5ef7e829f1845c89f20ea53522338da95d9c2416243facc505ba7ac050eee4a82e872a4a1afd4acb733c08f05defa2e32ff1b8a50a7d96721ada3c42cdf3702f65339a633dbba3ea894803c35c9714a2233b227d763176154056905a348d80a5e78690ac443585566c0b85c7f49d5b4bad1671821a084701385e95eb88269718fd786a125542a3fbc09562d48650441538bcc412b069c0dd1109f97686aee57deb6c4f8c88c1043407f4415364f6169a7c18edf7b0f333baf1cd669b63b94c8c0bfdeeb3b09c4bc61a384f1f97ca9cddbe73712066f5640e905574c6446eabeb771d158796910514011e341025d1b0d1185a9423b4b72fdd2994afe0c8cab57a20ebeaa034c92a43bd05da170ced98c947510dd37e772a15688df239c446bca73408dba41c979112751ba320663d127230e0dc110bcf92048545169da95a01abe638f4bcaedc6335861c192502e84cf7d352ef152c16dd095200a1d2bf88eba53267eae60e5e9616f0a1305384b81b39a5392dec27521e3dc57a4fbf944634aa793010dccc3919e957b9c36f4fe65f5d802e369982d0d89a4e0438b24aa72d9c5cd102388fd91bc2e0851c672b6837de127812bc5b95a2009117ad9941f3110208c5b9e4bf441ad075a30152551a37411b7a538dddb5569d803e573cff46ecc7783d2eed83abbf4ffae89afd46267875af6c266eb3eea83dd7fe4c45cf65293f7361d86356edca982995e206c37bbb20e215ab4480d7ff7840525ccf684410a4ac8cc888576648e1f786bb5ef97d58bf2a96535c554f6d8b290cc748b998252aa0c29554eabfa88712a5a705f40f8af03ec542873f2c16d9bedb1abfff11e943f5613a2c2a04b1a710f73907c2ec9f66ce1c3b8b4df0a6f77cf33ffe40e8cb05be7126c4be441bccf7a37ec74e2552129066010a5899eb635d07487ead1d04241468533a1fc375a3b24c3c8329648bb69d459f573c4ed26abb90f7923331a26bfd785658a4093b13b6968a19f30d1e78d2b62687ed275d8bafb2cecae9115d3e190bdf1dc2c4c7462fe890a153c2678d0a998a8f4f039137ca1ba178d2370733b94fa5658cf541c83b86fadabe39867c2557090ebd671944e6f61456de21aa28cda09c9472a8f2612199186a11f88ae281696260e178aa7a270f5638de2b72d65b2a8418a1a17751204e2a0cd94a5837fbae000cc35ffd96dcc07c9ac581524adbd35e79d3f6875d05a15a74b3b11c0002f82e7a3c0ab84d3b6000406cf747eb06ead1f4db23c5e2b7982074fcb726f5ac417e680d420548779d94cf83a78e5bf154d3663b94231c3c55435306904c661394441b1842de53649026e12cbda1953df1ba5b42ec9c730d3235988150061f247461e63ffaed0f0224ea9b2b2af04aa07556010b693560bf642224e592c9f6abfaeecde8e5b678e66c20609445ed795664f3aa6b16ce6c9345ba83903ae1004e78dfe87ab9b54704765cfcdbc8f1d2d0257310f4dcf8e939b580300ad676145e0fb1e1d5201643d3815469eb55076b993b918c98a5e78007fabc61cbc9c1da7968247e99e5789cfa4f226163a3417083f57fae72099c01b234214ee2adfe8a2703f968961258b29eec356fec3ce7b65b5127c9a19d1b777fe55c2cab13588f0c10f749119c34e64afd3f86ad8592c9cb24260d24d8d6d5af6af0c3251cb1db02131db782fdeabc0c9758911e140538c6697eacf66d3ca0cb9862fc053deab492b7aa6149499d565cc94780bda05d85a5cefc5b480464cdd92800f2a3c9582658d58183644453b0ca3ceff4ec33af7439bd5c8e6e019f49dc92608b4322e46281342ed6c6352cfc668401cedc94cd186b6b1cec1701fa7adcd954c6449dfe5ab730630ed6a2fcf76cfc6b478114c078af7a485739c1adf3c236f964e936d02b9a8c8161b6cbdc38c5a71e9f3e527a0a5d92e095d7994e8985590929096f310fe85049d577884e7023196d8c394ea159a329a7caa81459ee4697ffcb28a7211c5812f1582d3ffc254627d050563b891a701ae721a50a48a49e6fa318078fdec2bda2b68e8e291cfa3374e7908145d44b32121bb48791164b46d8307b4ab425470dda8c73051ccc999c1cb3ac269d71437df9ce338ef529ab5916624c83aa965ef0806b0b45a164a6717118ff14838c28cfe0f0cb710612f8f091c1035bfedb34de174038c2674a16a80b94719e9287c2b7be0b7ac73e8a183372e564bc6f3ec11e85b8abb525342f29ccee26b86c633427ebad6cd5abbb8f875bef419f70ca4fb148933f368eaf3b8ddf25dbbd04ec6362c709d74841fec93ec01696dfce8e9e33bb5faadce93dbad249d0cc2cb12328bac946bb08947d57938af68e987d64905034f78b85a42b256ed46f07c5dd3596b5c8bbb8299cf6872d524b2658908f24e1610957c6c4c67911153de9655ee4ae8c1a6a2b0bcbe230518fc838ae400fcd538579dd12a2148980a590cc98da56a62d1751220f71cbd318e02d7cad667add9a2684a05f2b993cdbeb4a2d5d8dc12dda6e48dd0a8780b129822128518b5035394730ba49a8a2be53bb735ac86a528b9b1775fda0a37d90a6900a691344ecb4773f09623986808b8fbcb6ecce6531bb64771f5bf8d4bc661dfb128a89e226b8e01994e9c7c16caa11d64efaf7874c949bd5fddbd4a6a7d6d8c1b6beebff83207dbf85b9c8793381fd94809c1d149a230e4b25ad054c0970b76aa3f54df806090eb21ff77762ccfd02637d800cec97a2d33a057a1b0af5fb70377ab0c6bfae55f75d20b979d35663238666ac174c81f87641e33e9b3edd75818af069c3418ba4a17d18f5d1bdac961f109f892f6ce62f21255a424c01e804258823e43d87cb7247daa1d0c2159ca9e2c3288811a90c2951530f44dadc33add189e2706d199b9253ca101e50f367cd74eec279c7a8d05bf1714f3390afbbc56f13a8568d3337f914cee5a6a6d6af4929c68cec4a30ab756ad639c942b43d6738a20ca67ed20cd931e6160cb45b0649880b5c259b3ab2a6581dabb2472c02b0b36c329aba3434789df273d74820f3dd7cbf4f589c240e6c135c6ec41ec2dd3becc2dbe21150b7f121987d3e13872209b192dda06943cf4b450ea0900e377b49c2d9109b130e12311149b273b4dae8a084274da0051fdc2cf04339e54becbf314f23cadf509867d3896990571777f405aa787e498b5c9c2484e844f71c31250fa138cf988c3c878db2b3bb5a4c1e475027f70d8581ab374f342064f32409c1e9c23d76f9e22606ed1de7a0cbddc8d75068b519568f58b2f63a515ed80ae14146bda2fbcacf54ee97077d1468b972bf5fae2525ad8fe00e00645ee1f65daba346804d2e8bdd835c56aa8b8e6f0b1bd5df86d13ce2c2807deb082aab166b4888a301152668426fc20cac8831d258906d5dceb45738c117c1c46782b88eb616d6a9de67828b32a3e5ea8e2f468de51aac6469967e3dad6d092d18e1590588c09ed1990079f1f4d38b0109bf373f152be0da8722af3a1ea4f5632864a02be300c4da5fb8e30a39005e0671be5256e76b6bc9e5779605f7c1e7732f084106a3a0ce51c92bbf2a702d8a6cbe77e926a56f566acad276b174d5db1332e29e1f010149ef571ea00503dffe0071bc5c2f9740a0f1d8a9cf03a46588a5eb569aa898eacb61a77c5dd29a5800e0554ce4a51980ce0758d33380bbf5adb1db1fc012c9cce3b8680b9852ba3efed17650ad913121bc915995f07226e4bcf6175280a18620de7f2280d7eae417a7cec0477da4a25e4b48de8a14e6329d7eb708174d408186d9c0e46651e77473924040fdf5d34a717f713e8a1bd7ed942a365e6f27cb28515be95ab556351c79144170cb0061a7b45de846f238ea68ce17f8c2169f9f8039c32037b65fcffabd358692c8bc789728d0b2a0d88fb749dcec9416d1eb11eca8d10ced0e50368b86d369f240f2ec9675a3958706511ca93c40345a776f01bfb1bddadbb4386884edd9aeace7ee0aeeffd9c360740da19a782bd8486173b6a5c22937b4024053b35598daa0a4c507e497fa4992f74ec270298b3eba1649de9342330f84760bcc6560b194d022e78d96ba7824b1b519e4dc0a5119d89648bdfbc3754767aaea88d214e158ac652da2ddc40466d4c1c35db877123d116bb63017bab0eb2cbd71f65cde79affd5295af2bd433b66d668a1c8eee26c82bb29a080bce12189bc145753c9833f76c505ff5cf8a3830a63edda9a01cfa2584f510dec380b5183053dacfcdbeacc3912bfa5c9558d63c219d794f0a3ba7ee8f9943bbcf545c21dd172a1047c1be9eb1cd67b043842a3938d4b75daf2d8fda2c1d4a7747ea6b9c3d9a339b2c5813206b5d12a9a0662c1b5934f3626a8e79246969622d99d3707caa9f35d0782b5273fb7d0e15aa807535b71eba2657e41c8416237ca9235b11912b4a3b5a8b5a1b281c617b48801edbcf9ff5f1682f5af2db4552a743abae48f392e0c3aae492533b1bddadae590d665750fb98726ba0235dbce4e7b532ea39b005e770b1120ae48c1ccf5f6aee4f5bd78af8d90ce352ad0c59be4d0a3b072f178d5c849b03aee92cfd51966c6dd75b61d745941765565edd17b3457b73bd0bb40ed2d1688d6acd68b27c70925de7685a918c54c256d4f1c8f70bb0b79273e9e8e5227e364177b3562163773ebd75fcadc97e8aa034bc68c4e1ec3a064013cd94eccf60cb9e71c42cd9162f212fdf6e2c4032e576b41a1af33c9f310965707947c8ce48444036111e638be700137776b1a5f22ece8b8db7d1c477c9c02d582095132f310925bd5040ca734206a78597543f58d2a4ccd2109d1e50e3d639d2f1274a24c407b4818bc8d163956ebe0fa2142ef9cfae6b344092ce31ccdf7626ea6684b83bdbe666ff7742d239f0dc3924988d7251d191c4fffe923736ea8d0f234b72a4ce2b8ccfc1dd87ba9b2f570671a4faeb167838351e5938739f75cba169ea99f8f0ac9f7e8f0cea5729f2e42cb9fe8ccef9b721b387db9dc51a9fd2135a80c82d78f185b5eaa0c2046cae8b80077748a0ef299c082d5ae9891724cc86bece795e3f8721cbbe2a16741ce1117efcb787c8cdf872586d44b7552defd1f92ccf25f5594ebbb7f4fef58498590f35f25b64205259c3c6175b750d1bdd7e2ac911b6700f14e3b371f86ad5adf45ff9f0466d2af820c93d0ddd5f0a790242b5b37a54d6e91d97af01321781ce70eedd1ba24f850f095a192f769c247d59521dbfd550ae9ed6eb6d047a4da8c53268773534d6cd10efe92de7c5b40b717940b5a6cc5f2122f5193a4ae9593fb91b2b601e02e3c06500c4f37be224a744bbcd7e909a9f723ac24d2e7e425089a9ad0de36c7c713eaee2114d7358c852d016494770efd934ae0cc0f627a0f65a87342965512008b1fbb886a4c9fa106c3694927611c676fc59d77bb1c15403588d28e805848ca906ef06868a3eff1aed68b84eba54f7fff9160c95617418d5f8f50e0332fe2129a3a7c0b9b25210515a74e16e92071a31fcdcb9f48d0218b0644d641f8a971e6e0a6325f002cc5440e336e4b1525c85f447faeb61aaf067ac7fffdfd9dfeabfe08fda7481c55701a364582dd467ed71a3fe924ca6d1fffc7ff3b7fca3fc1bc04ab63304ee925055c05a32da12b896d0e6fbdfc054c9524ef21790255326e097f811dc34f003c4bb4ed4b8dad5a1acbc16ba7b84127e2ef0bfa0bf9f7f027ec9cd3a3f269ba9159418912f9cc622413fc8e6d0a9f458bb980a0a55062ca5a9ba8cdbaaf24904080bc429976ca143c947a3f85eda54155ca5453183abb4e8e631bb84aeb5f90fa4259605e2bf50b92435d19fd2bf84a1d2cbd4e557f894301697bf193f824e8945a96e4ad06fd4ff15b2a57dd501e3420543e4198b2ab3485453ffe9cfd44fe5174de015c41fe9eff5fbff3efd0834253565fdeff3efff79fe634c066eb790da254da902bb846e6aff1324255606f2ff50a1645a821fe6fe0250c9dbe43635e45cf3ee3ff89618a5c36ceb95ccd83a3654a460a9d1fe1be84aac0a9dbf802b61335d7f732603b51b427bc9bd2ac82d79d3de5673b85057498ec8ba45f85eb8510e4bedf975fced3f2b7f8355096fa9e14770969c29d1ff098c926ab3fc6f602e092ac3fe02d0923d36a1bff45e53f89778163c7f01af927966d02af96b18c02afdca5afe02bfe46eba7f992603ea1b4879254c63e747805de257cbad27ad3209340b4ad5660e99ea71edd548e081b0b82884498ac61f81a9e4a62a5897666e09a89253a3fc4cbc602a5f72c53ff0aea7976ff34365b639602f19bf0a7a2586e9ffd9fd9d29cbc2e647c02f695fd6fe06f8255665fb77fbd9fe3742595a9425fc056809656aff6b0029f1751594a56523fd8b7fcaff066169a1b6115fc96bebfd3bf029fd4ddbfffffffc4bb829a9952bfe02ae244da3505632a52a484a4873ee37fecffd49ff1f18946016fa47ff9ffcbefcef3fe31fca1a7842255481a644e166e0940431097d4bdef2f9dffb6fc02fcd7b157025692a045d894929ff37d82cf529fe8fccbf035dc9db55b029f14dbf9fd9dffd67feca7fd91fd76febbffd93fbc17f647fffefee6ff657f62bffd93fadbfc1a5b4045b382e991be8d7e88fc0b7246f156095cc6f06ed927f7b88b5f42bb63f7c7e854f89b56cf9bbff863f8a69a1a292fe8df03390945c97094007516ca90eb7680230e27305f60fff9fbff6fe4df6dd345603ba151a80adcf0b924290aaa2638c6c5d6f1268cfaadeaf5f9a3aa6495b0eac3a7aae35269a6115e3c97d7ea2bb4eecafbf3c3119249bcf933ec791c7394a741bb5027f2a54ce3c7c2887975f445f0d1c0afa1a344f6a49e0504fee385d2a7dfb36c18f13b146f047ee0af794539c9c4cdbc2be48053ea76312219f9ddf2e4a7b75820035d849e0c6929bd8a8e49d964eac4e36b9d8ef79bdb7f20cb97d88bc3431e19fd2bf03e1523eaac05d72cdfbbfceffe25649cfccfb1168977814f74fc6ff81aed4323bfece7fa33faa29225eaaa8b988925b5527a054b548ff597ff87f509341fca6cfc7ce1895cf3701cfbc3df33c0ac848787a6830691c30abe7f3bca30cd3dae19658f0d0fc86d254308b8eed018c482f0252aa4612cf8a85f5f4983eaddb879ed35bb4c4b6be1d6605551d7a04658ce16446b04e6cd326da5ad06b7a9705e90f48740fec727c93318d9bf326936c36d228236c946ab4bc2c474d77fed3a500a474a2d64e9a547f3b9341af74fe6fdaaf28e706737ecc6c9a81a98047be46dddf566e8c71b0dbc50362a578aa55f34faa79040fc609033fc73c607ac7149a4acd24b228e7a71adb10cc5b92235248e80b57231d37be270cc5eb44ca2d0aa5ea8c021bf02d2089808eefcf8287bdb3230e43f7f072992c1ba4cd4fa6efe668dbcd0d97723907343028bf65bd8373875f156fc6a780586b3a85d2dccd303c44bcc6bccc11c1a5243c5bdeb34cc353cf7af36461f226ff21309b2109071b327112e44974772e937bd3d0ed61e3888cbfde93619bef661d1dc75bd32b51bed73e7d67c307661c58ab827199806a1525e3c96e270841237be7d506dec990079c2d020f97169483cb636d284d7517761b769aa38f408b0cb7a64053416f45a22eea1e0c177a81380d3b5e14367339bc9fec281e9a8492df61d6729d95201df768c1d6729db593d6274186a67492295a03ce100999fcd200f1c50def717ca56176b9b9c7f8a57a465e39e6f99d36c03c18184c323f4592f4e9e0be535b3ec2b48552c63a7ee9356a5fd918e18c6bdd334ff9a85ca9cddf3d94c0ad8a7793032fe0c5dc8d1a45ab351eca3ad906b8d134dbf177bbccb20d3567ae93245cc49bea8007173574f2d9260de9be91245044fa4d66c6e9c391f086c5432d84df6387f7ee9389b78187ad545da8fa0231491f4592294cbb1c561a624ca266fe16dec98b8bbd685f5568eabbcd74e858118ac7910da203bdf7406f18f37723f8fd748ef6b3634a77800653182e7ef9a0ff6dddde3a10d1753544852cbd528839cbf38ab8779e34d6d305d4ff9a66ae7ee3c419126ba9837a8075f2fed3dc84d6af27692dc25acb26652413bc0608c0e046fd6a56dc24690bc5f98318cf8c1b2bf0b1f978732649146ec87d3142c82af7618a6c5211734f58b36f54da81732060bef3201d9e58ca941e13553a76874c2f801ed2df2b68ecd0548b97fdcf556ef17cafd1fed4d901a8dec91aed130c164adacbc37994bfa74491012292f4abc43cf9acf48d12e1260d1918821026b20f3a705b529f2f166e328887a1ec306d7a273a40dcd7cc4921f7de71a8cf9eddfd0986f42dabdcc81a2fc16613b5da8f955d67d3294f876beea4da85c8b88e78087c379da38f66bfa1059748452a2b8c21354fd09b7e10c133e41d14157a4441b39aec48f255189afa25e860f246d80b52a8f9df5474088918c3945e32b3daac187b7cf5e68603b1daa6e921ad6e7587c523aa59c62e51fe3c4ed1e557a5ae658e0f8e5bf764a71e2c1094704e490c7fed309fe3426acfa1f399464def788b5f6ad70276b7fa5a6df97ed4d64fbc29d02fdb3394fa9b10f4df1596c3763077b17ce617308f4914f9406d722e25fdbe5b32d26150dfe31105dda3f607bb2135997ace2028182e07d668abe402134050b87ef5dd5e824c3252eb87bb8292c62983987ecace121a314ae07fa6572c5841cf35ebed6327ed8c9e0e31110e376a2f69f6a9e76a0358cf2643245dfdf0f212a270ef27722275f49019e6cc1658fd11382934cfb2b94852e999e0ccfdbe63eb396d8cf570bd87e8aaa5bef798678a475aeb0e09addbd0c7386c9f097919a4cfb1e714a28726655c8196257b02c693a92d6f975754c2db7318c67527321008a165bb00d1aa46c71685ce963b0b4d013b1c897c11b80f3ca428b69afe4916226c76135213e26f1e3a4f5c147ceffc12aa450c50dd42c325d4ad0b0d55c69bf90d72e4745e6323a10247e7fbaf58d2d3fc2c53cbe4057108f45941f092d379ddf2e31b88146a10f905ddbc39b0f051af8894f3d603e4d69b6457d641e66f53ab781039bff5d7fcb41127b9b8f62824860fc073a678493e0cb8adc73d3835c4909d92f28c7ccf5c1c8672b3c1e339804d2e50d21011868637903f1430d293886c80c50d4befa906315bf0335f79214f7e7d46fd0d3579d1341975c4118b328f573fd140040d3fd5c6286c6303d44090ecd069a10acb7c6c33327a1f9ab18e22e02531d9276e6980c758d74bcd2c4bcb4fa5c86b1219ecde21bd3de2250f857dd54316e132bbb5da4903a3ddd196d7770901122e03f89f777352a70034a5a3aa8ae3254e545cca423d31d63c79a064e974a480008fcdd1cc8ff934ef1c7af066a5cf35b1feb908364696aaf373538d93c0ac3be3b9387e7c5d7c04ee838d42b5f546aedeea14e983c89fb9bf66eab48d2e7ef1d59e9a131791ecd9eba7ecadb5549e59282651493e947d0f511831d4d0da29f1acc1d120c15580ae301c48f8b619493028961d2276a4d471c08d4ea44a9f140369bc5b3a5f15a233aecd7b69ad3d11eb48350d00dbc5f2ae77244d389b19936faa5b81226b044808b5d2a26f7cdf16aa0bcda3aca6602b130578f47ca7217b430d147fa90142b96820108803f2355eee20e3ec6e1ad3b98093ca700e1d25593985950ec1e9988cab174c5c4d1d8fc16482228741c7d18b7b23976a2dfa3f1044399c811433ee0e615e914a6bfa457469db937f46eb441b75daf5fb2023f9000741d472ee520b1867c6124c0d396f09e714bf6ac0b2e5f5e0869e084fddcb8e9287b1a10456243b1ad8cd0ba1672e6d6528f06c55cfb70cd4d33755782f098f7079042e8192e806aebb93c9b85813971252cf71bea92360e7305d2af144664ffec334f214caa91b6022cbe1b568e0f3402eb911a8ce19d9b947aa5c6c2aceb6387d94f8bd6fd126abe196d582ea764b2c17132097ba4da3981f4df7e5ca967fb8888201e8e535fd5b4719fd575326c6c187f7ae164169ce8164a2cfc9569138328d45e531790df645253e4371a6c95b683717c1e910d55b2b008a5b3c6a53734328d0225d5d77757fc6b8eab27f5470f659c692f975e34f1da73f2e9ee70a057ac4c582306eee73320d6db9841c7528da4fccb2bb081507f5ffcb774eb6bb52b87a65f673bf12427e289b3e1d29ad7e0d0215f655b358cee02facd3f351d8a70192d9eaf5a129a1ee9d42875792d168fd8b84755bade6e855654086d892e350cdebe0a9009ae57b23a705613bcbaeaf413604b2c114580c9be83633c4679f01ca2e26185082063c1146934ec5edce40fb86e1be2c92e64c38049ee0a1f47d2ace0be01e095d21f6021a9415112d0e24ee102998d6191d0531de26bdfd6dd802484ec0ae527148a3aea7382c45fca03d620403c268d822185c9b222048287863dfcdf86ccc14a28a098bc263222560a0f847cbf4d2fe8da56d80c629d964f1a476661aa57dfb58bf181edb639d364fa8bb587ce07c98a68128a867e621a36ef0861ede223b5973143d020e0f8b220e63727f38bc03c2903ef264b1528f0561f561ba61da91dc0b38ec0c4bc29f6b010a21e0c437cb42962eaa5a1a03b9327872f431e9ee1837fa108633a1d8efdf0c4862f09ce9466b398b83949228211e3a14d998624998988b71e2bb8a483f285686f87bfd1d850a348016ec7ad884ae9799105d4c1b941b71beff34f681276148a51940b398ab15ee24e751b457f9acf57b8daac520d8b063cbe75613519923be516a85591bd2384f6d7aa220ccc0f64a7dcc43f490d83d2d73f34f47c6dce57427e0dc9054045e368ae0b1e17f6b783095488a7956782bf53c93853f51f2282968f30045c1fa378888e2c661b24ae76887dea357a7993a7b7e633b0484ec516aeb2a57735ed63223e195c0126e88a492163ab1ec93f33a64e87d557bb8abe8a134a4d72cf9d1b2f743d4c665e104aa9f96de8347fde21b22d568da941c1137915781b5e857c13a2928c2513ef00687b7dc07648f56f4710ca22f85337c3125345aaa816b96825d12c3aec76e3b5086cd8b7425ebcedbbefa0e992d33d72402e6cb7e521f22ea544db40113f037fd74d21d9b8d0b33abd25447f2566e209e514804bbf34612fa768b1c73a0dcb38ef49b614dd47b74666014a4d53f143661a445edf4e15b944d00c869627ec8445a5772ac4520c0bb3f1c4546aaccc0e712673bd04a37e717299e0de08110a30e7e6a0c46e402c3ed3d9d195e53679466245a047560f82ce7838ecc3604ed7a3c0ffe1c41579aad3c72faf9f6b6232e91f07199f1693338696b3f9c10c8d766712290567db5a8fd5ee29a2abd9eb2a5dcb331ba15c56086215583ed953797f31690cdd1783c4ab8bb54e1c4649c01b1fb141f6a12830732477e367bb4548d52bd20398c16c8e1cd95954b12b4c48ad93a50d478104cf50dff7832179c2c3cc3eda16d2a0453460d4e91b24ee4c1e4a51e783451d19bc4355f3348708e3132f8afa15f98bf7f12029f62b1b2ee666d7c4c380c1572102dc81405864f01873af3be6241a28ff34e7a3bafdd30060ce633e59b488150375027b12b38521d859436e96942fe2e86f8a2aa0a25cb4f1c698eec7aeca2679cf20758a19d3e5d289b0014de67e96883b240850c7091d34d1cb4cd0b25cec76754371303cc841a5342b612ed4a4a74b435badae9b0c35c4a7111cee4d43b72129b286728e8ef18fe8bb6e0376fd2d6384568143ffa5b6abd9c4e490ef501a5d31700662aa461c01832b5d446a74aceedca731551bc625fccfe2a07d3915dfa5411d939cfc71dbc10ce187b7f0e982d7bac0185c96b9c3d3f5a57ec04d9a04cf3031c4df23ac9cb7ce625d66024ced0bde4f58ba5a24152b3b62485736f3f3502738ada30903022ca7439d777e126b317f984ec7a7c173f06bd68fbf83a762ae475be9802d24312e6877e1944b1cfce55158851b7a790b12edd9c8b7f53832a5c0f7f591adbd0a5d89e618e7e3bdbd9b94eb9f91abd3b9e64b953bc29a8b2e53b0aff1f7fc6893b6a48db35d1adcef55ec5162d21266a0f514c388ab92e8d67dfebb63825b082b90689868877a3c68bbb22bce0d3ea5099a3767489bfe06e0f785c59c3806855a0d546ade91a73345125a844741b68cfe5124d8e0db227d419db1735e2e0bd7db94d998106e6e91c69d4539640ebabcc851870de8465563d780cfcb6aba69c061cfdce350802be0ea2f65953ac3462c9dd5d062a0d00174e45fcd464c5882102bf5b11c75e07a977070e6c9ee0cd0cfcde057e7908211f3d1018d4e601685f76cbe68fb7d02ee0939897ce42fbb2799eff8f30ea6eea1e6bc7865ba9fd1f30b5f20612b8428a10b923802818806128fee788b14652b21d1523eb203fd3ca6cf046f8b9612948f033e7ac05583f738599659ac09f59659a30c97ef16f0c689abf9f821f569509b4029ae66b02cd333381b9c0344d0e66d94f0b88656655993fd35c33cdabaf5f9759f533adaaccf8a7c004feca4c20f0f7abfa5599f1ef678a6555fd4cd35c33c97e5a5840b32aabca34cdf734216848c001b8c1ca049a09cc2a086682046b0a604428332b5499bfdfd50feb9b412f548109d3fc993fd334d33a564f76193bcc4e3136f53386bf140806a4499a5e4c8864a68596479dc50445dd4e8ba06fe701f53a2fe70a1da45b059da7d1690e922f93d3c286808354f6e904a431348604412df60dc6bc901898bd13b44c179279cabd4b25807ba2ea698f4a95a13d0af343bbfb726fc8826ad870a2719270772e90708271f52c0f64f43bab26fdacfad13bb0f7fea2c305f15ceab872f8eb9b2e22970d1c74f3cdb481987ba692c10253419f8d5f293b37f8f6bade9d0ebd2b85c81bcf2b6bab395143469cfe80423ac08807a33ca2eae49c3df0497102eb53871f40a7a26bd06104a117645eae59193c369a0b04d4402e10059103402790c93c06e2f07079a90d4194088bd2004243604c11e2307bffc13520e102f0cbba234c8e1f348aee5431ca22042894a60af92435075ef029a0232a0984513e981d820a60032048815204468a0348d8819a0e0d0966ef559bd28e22600421c3a947efbd829007ee3abd7bb233aa77658edbd163878a9d2676f2ecdcf50ce7d979164f9c439fd22b3d913e7dde37db016147cdce063b64777745239ee1f4d0e13cf3d0e14416f1fe2c432814c2d0c4a8f773ad0e9c3a5cea3ce9e79ad6b996dbdf789e5a676e7fe31dfe8974ee8f1e2678f0d0bb8915ac435787edfdc48b27dfaf911fee2d78c0157f1bed615fb1563b2e911e3efc3ebeab1367562c8274e220953fcf7d524d99e3326068a165d03a3bcb71e6d042d3ba719e6b27da79c3fcf56e9a74420885e8f9e144d9b3c389e745bc627d23c5040da081579cd58e4ba4d6da991b10c280b16e9c7dbe3c1a60f673adfe730ae5ad54ca7bb752798f00fa3974bd1b8dfe88abd55fe98973bad1e7c81b2f2a7d9af2e34b398c7aef7df3f9e51820274c3f737fe6fec4e51039f97efde5d8c9693b95b77d7e3ca457f639d1fb8fd34fd2cff8737f2b870014e856a120f53e05001eddc18810dd1492a5770cc1de81977aefb5c980d9c8647436c48b427b394f207356d7be4c86b37fbc717c892c1e85f5d9bcc48539d28760606a9dc990ba67870e677a7676d860668ecead845bdbb084dbdcb0858814a7870fa7bd9cc7189362d0f3c3892f91c501d0f3c39923fd5158e7e8bdf7e8c13797fa122f2af1d4faf0d4e85fabb9d1563b2e916f550e3201400709c7d15ea236602801c21639592329c2001d808a0aad6057ac20792382051ea004cf8ada8cb6544bd0805ee84c98140248a6519a19f0088f3614336c800114c41c0326c99c9d89660ea032985dc03a4bc12aa5b4585970c55c00e45025d88552034dd20c6615f00c248153f00b0b2068a6805747c628e1d42684153150bf4c71f4dedec074690b6afa052a550547a22e60da3a4cfab5099348d05c0c496a0060f4ab12290b1b465224a06b857e3da67cbdf87463c29ca33e3fbbcff9a70aa1187c5eeefcde8898fa04f600051191e4fc52378ce8ccd13560c09f35bf2f08f004a0e432054b0c32a6292f55ecb08b622416ba9890182081650e952c434956cc3c5134ac891181942c6aa09864f4b47309e80140860724a61321308438815125013b40ccbd357493b4f43582b087c38cf14002be1b6b72e40531476c3972a11200a7bd134613832273532d964c82c964e0244511ed4b044b2d7a7fe08e0c3252236a9005d03ba21f79b294888df98b7a9f8386810f23d153264cef5b1ca0b7a485ee94c2d03b112c2a305c000b8082dcfb7181417097310df020f63eb74487fae706a618c9dee103a4fcb9b107cb4b4def653d40c47ca3a02400d23bb00c5fa604c8cc7d41aa4b970a7909f6aa547fac8430954a8d2037602d22857d3450a9a82091d327c11d47a48fa6543ae450caaa9d6c89659a948a3e3584cd033fba020d18a57213636646a5883b2154289570528ce01d0d1b385cf549d5450544243c816eec20730715293f682cd4fd346d2307952323599d46d171e095b706d59d02da924f9684a902880caa01460d30044424cf01a50baa255f1ccdc1f286d29b255650e58e48ed992533e8980305d50b00e082206902e7421d125420f6c84975c7d641e780a0b230641e5a19719c38820395161b3665c28849c5e34d7e8ad3a748a39e308080a70bc6530b701963b760305404417d0ace223246b31cb31720314e65799287e54f1c0b7b00a03b152481466d18c408f981f3762a0f889e428e06e101c1a1ed541c01305e290d2d014a3a9c6a39b15a53c03490a1c49e8a904508203f52e848018576aa1fca83f0b1e0632d8f99a94a4c00c074c326a6a81833c5c74e108a9ba294dddb652a8d8f0e5c7d32dafc61b1c0140066fab83509c137774c5565ea06262b14f62a6c3ac2c794297fa506992565beec6103ca1409422b629ca57501fcd0640a2d4920096598ec30d56192a92d03ec9a701291676801a35210a4da4b63ca00398c54142ac5684c1215589e00f028d07befa8fa94aa53490c10d08398ec280199045cd484e87fa9cc71a5b0be8f2888383d3f9c4ae592d6c087da69881c9748b5fc68df69c894f7ec7befa67aefa57aef11f4de49f5de47752bb96ed5260a8250bd7750bdf74fbdf7077aef9e7aef9d7aef9c7aef9b8497d0b405d19838d7f01ab9bcf3615e3426f1d3438713570a63619ae640ef5d53ef3d53ef1d53efbd817c79f7f93732e60167de3a84a0993e5f6fb8e79566d0f44efba4a72df5de2b51d211342376b46fe45917ebf3e8bf32ef334eeabd9b6bcc36c13266c13256643af5de7b97d47b8fd47b87f4a807c998f52099b11e24f3a067c57598b095a669bf95ff45df08e45dc48b7ac5b6badb46bab5fd4fe2872a3846ac587f5a7993a0ab7bab7f8d2cd2f89773fb72732352d49f887be31c8bfae7bea8cc7df2cbd575fee772fc7ecbf1655bfdf7ee1a298bc11a20ad8769e466347ab630797a1fd2e8977551af58238b7c3e85b098e35f79dbb53ce123ef628df4feb48d1fbfdbceb58f2c1acfe39de7f788b3782a97f9c7ebf3477c79f7cddd6da7f2a6e18fa73f93cb13f993f75a1e5e549ec8bdbe33f7e78d9f77adfc7a7fef36710aebff954bfae3c8e2ddf9edf37be43f919e46bb95dfc6a9a1cf68f45fa9c1763b27a890935f9c70224cd09d72329d0859aec4298f38e9e6ea529c42e44fde27dc79a796b6b19aaa198dcbe4ddf9ffdc295cde7adef6f45716eb901e0df9e790f789e7d1b7b10f29e214d6c5bbbbe55d9fd22b73e44dd7451c0f791bfddddd89a3e1b67dffda2bff71c429bcbbbb71a35fde2e729ca7cf7ddb89569f681d8e45e25cd2a7f878dec3799ee78df33cbfb1983cdef48dbcbbbb3b4fa17dfb1ccf97f769df423c2f229767964359306038d7eae2100db7bff2a4c387b7f2db18226f24964158ecf47eaeddb98915c4f267e45886b3bc7b88178519729c19ce72e3d20b8527da8df3d442bb897b67b9f2e3c8e2dadacfeb4a0701bc740160cce1293d910da95cce43a3cfef7c880f8d463fbc52650555efbdef14e2bf6854fa8f8617711f77e24aa1d1f7de090deabd67f57eac927ae557a38b425418af696949fcdb28e243fbbe95c97f2ce643fb2b7bef828c43e5ad54fe3b1f2697c3de7b2040f97dcb21ef62fd65a3ff4317f37f24e645a588debb9fbc3ef80d87872ccab1f14116f18a4a15b51fd17b7fa3c01e3d792a5151d0fb9d7f7cca1ba3d1b309f18f9fc81b2fcecbbb2e7ae552041af2e3a7c78b79ca7befec331a4f7c89233ff2e337913e95cbdbc529c4f165ee953e99f4e7b2be95c9538386f31373db2ed6275ec49379f74d3c695f23ff89bc93cbf3cef3e83b4a2761edcffa1b45f42ee7c99a286fa220e9bd0b3f8df469ca1fc52eca5beffde4f347bc789e51e22863e787f35c93a398509040c1d13b9461ef7b6a4f00a83f5ea754ca792a64ba2768bf80a143cf6f64ef1d8fe9c18989ded1a4cff163316f6264a289c68b471a2f1ed598c04bdc74a371b9649513a7f70ea780de7b9737375f5e67abb8f9f23a9ffbb63cfa7ea31b0ee154b8cdc9c97643b9840839157473c3e15a124675922b1dc9a3def190457a59efdd469b2ee2dd6fa8aaae7b5eae76f3c77bee236f38ba147df41120228e40c8334206570a4db9de3b5dff659911f45f0ea3d11fe7ee9b6e441cc3f11bbb711bb3f11aab711aa3710cc3f00bbb700bb3f00aab700aa370fcc2effbba6ffbb2effaaa6ffaa26fecc2eeebba6eebb2eeeaaa6eeaa26edcc2eddbba6ddbb2eddaaa6ddaa26dccc2eccbba6ccbb2eccaaa6ccaa26cbcc2ebbbba6bbbb2ebbaaa6bbaa26bacc2eaabba6aabb2eaaaaa6aaaa26a9cc2e99bba699bb2e99aaa699aa2698cc2e88bba688bb2e88aaa688aa268efbd8b072ebd77b39b05f4310599818828fb8f936be54d7fb94f236f2411f726f2e84fcc6328632063d831e6183162508871c5e0d737edeb7fae218b3dab42cf428588c1db3515a2c2340d79e33486bf567e9c0ffd5d5de9bd0779d17b17da5f791adbe450ec7db8fad7371b9044bd7721102be5ad54ca3fd0d0198d9e0d6ee7e6844ae4de8dccf33f899f4a6fa47f7df6e5ca9fbc95c99302dce23885136ee7e6cefaff5a882c7a620916739b109f7dee6b22ee4dd41fb9fcb5f13e3b9ea7700dbf709e77ff78cfdd9d4262f244dec4bbd3be4ffc2b89f96d23ede299e1ecfcb8d193388577eaba3c7fb22fbf3b73bf54d29db83f7ae41dfd93f8ddf995773e770a73fc3612f333bf73e44d23efeca21bda07ba0eca9b441e7d1b479cdd9dc41cffca9f7f1b593ce245a427d2ff78e2218b5fef1b8b463a2d27e2158d277e1b4fba98e345ba9814f181ae43879cfe7aa7cf70769efeee035d07fc68341673fcfcc8fb1f6fdcbed3f2e57dde78677e23f9ee11a7b04f791fff310d593c3f9ed21b4f7f7e3d4fb4bbe36c64f1567ebd7fdbc853782a7d12ed149a3885777773a730438e6348fae3d9899f42fcdbf799f429d0f06ea3cf802c9ee146e327b23885b8b0cd8d3e977609bd40f5af717220e29e585be9bd9b73fac00430a52f481f5fb0af461fb16fae6f455f5c4fde67060b67e79ada99c1c28966e15c4bde279a85f3ec3cd7d0f0f39401e3d737dd47d6073cd7329cf9b3fe725ff256e6c83f54e6c8e21a1f1dbe137c6bc3b53d48bd1e1a8dc5bce170987f7c98f49f38cc3f3eec41bd167a67fd5c5b8e3ad79672fab9b62472aec59d6b4b08e75afea8f7732d47d3cfb51c47efe79a8c1eccbb7046e71a9ea5f7730d379e6b431c3575ae29ef9c6bb8ef41658bdebbd2ec5c0bd67883d7e071aec1e8c11a63693d989c144ca2e9fd5c337924ef7af036d5fbb7ede59c2fe77ff44a9f3c6fe41af22ee2b51f11e25e6da4a5debb100f792bf7ce7fbc711cf72ce2e6900e8e7d7a4fab913e2d477ebd5fc178cc89c3a17279f7a9e1bf2e2ef1d470faae8779353e54fe1cf747b5bc1a4fcbefdae719efa4da5d140a7fed8f1fe973e59d3c164ffc78e63840effb7939fe739f477b258af32e03da997d0f89b827aeadb9ad7aefd1fd3c6f1c81872c26933ec529ec33e645ba6844def589d34562d22bf1ce4fcc4fe5efcb3f3167710a9377f2c48bc2a43f2a3d72791a8d224ee17de2ca1b577ee4b23ef1e392a6bd1b1ef23efaafa43bf34cf47eaee115f1e110cf4bf9721a7dfd5c23e29e581475ae21bff1c46fbe7c99b3f560b105ee89a7f2274fbdd3b315cb7a17aead09cfbb9e2c12f1fa97e8bd1be9bd9bbdd744b7fbdcd77f382c2287872cf6de89f4de4d9ffbb62175d1c7fd4f028f45efc725f29f77421c4f168bc6e4d1ffbdbbbb33795ccea747bbe74e218e7b22be3cffdeddddddc921c38693c3a9e1dc70fe6f0fcf4e0f67b2980ff36fe4902159cccf68a3a7871ece33ff4612a79ada8996e34be5dddddd29ec9b4fb40cc9627edec8b333adcfa8f4c6e2b1b854a6e5c85b69dfc1dd9dfec491771179278bf9e9e7fbbcf3f3eeae2f2f2aefa2327987df378564315716f5289c3f5f268f377d67f42aee4404e92a73f4d71be6f9bfb7b6369ce96f57ff1ab9bc47e4f9cfc268f46ccbfa23ebf872bae3da1a8ebc8b38f2e77c7ff6129cd1f9ec5d889f68c4e279ee7db8d30b132c9dd6d686c334ffa29fcadbbe9167102b91e4b535bab8b696c473e59d828faef727bdf7183d78c7a5e34a612b44d3e0169fef944af9ee3eef329c25bd720fb95c5b93bbebbdaaf7bd8effbc6f86edbcee782b49f4def5ba7db7a0f75e0f393deabd38bc730eab20e9bde33752191cde181aefeece63d7d2bb5aefbfaf197bb46ef4de913d3883adcfd8e78d49bcd876fadefb32efbde3bd77650d197afab976d5bb5e0fca00d1fb4f1a3f71a8fcc2fad73885777ee2c5f3ce7befc9b30a13823188b852d8f7f386464fdf4e94c098c40a53619a66f45a0dc0822ffa7af0c5dd832fee7af0855c0fbe907bf045db832f2ef4e08b083df862ab075d44d0832e34f5a08b473d6897400fdabde941bb303d6877a507eda4f4a0dd911eb4dbd18376780fdae1f5a01d8bde3b56f02a410307bd83752e2fbbb507edce1eb4e3ea41bbab1eac13d583759a7ab06e811eacd3d383756d7ab08e4b0fd629e9c13a1e5bbd2f78d0bb185d971ea47bd283747c3d4867ec413a193d4877a207e9d27a90ee410fd255ddcaffa23626976d2a8879f2b7a9c89749a41b16174c70a5f0e6cb97f99f13d1bb0417183aae147ea87f325954e6f8da11f77b1df2227269b6bddbe8e3dbd35be6b8b246f2461ef368148978f6afe9d9bf70d182855d1ddd9cdc8a38b8146f284e982841426e631b2122865fb765573545d5846968a93668c87086e18205b30a1426a43a2594918d45100b8300f6952a55aade7b83bb1e648108570ad935ed2b6f3a2d899b7dfeed86ab3f91f65f2e2f1e8b6fb5f2dec3711f2722bfe188b98f8b3b2e711222e0169fa351fb1170fb713fa2f6233e9ec7f17f342eeb3b45efbd5845acc233557ce30d571b6f13c83ae4b7712b49e41fc745b0b1edddc63f42d96744aa40d6e1357249e2d35e3dbf8d226e4c2ef3d3dfcaffc2e7c6dfd686d7c6db441fd2a7b86fbadaffb6393c64b146fe249f8c74937b3373d42980d7b46fa3881f8d7e59e39f7fb651c4bfb2f76e7fe40da7024f51ff5bf95f206f38b636e44ddf746e371d5eff0f47ffe4dc3f7a38fa27e7802ee698a4c2774d4b33d2cd19bd77630fca5de9bd7be8c13908354e0ef8adfc36fc379e785e9c4fbb48c471a530a77df278d36a2a74856b1afdeb9b4eebcb8b4a753d4ffb3efbbc52e94fa488140f7af06d500fbe35e1a0a1f77e01acf7f8cd460fbeb13d8882540fa250d48328d0f4200a233d8822af0751d0f5208ab41e3c01aa074f14eabd5fad786366f5deed1e8cd3145c21872f6b7cfc4387bed95dd3d27e6efacf461c91defb79e6fe44debf1a7174c2defbaf07558c8a8bd0fb71893c3d78382e916b6b6bbd77be1e54c187ff258e17d5703522fdf3bebca8f449b5e34d0be725312fe2a9a9ade1c93c279eb94f9e1ceaaffc78784565fe954b63d1dbf6af4fe0070c3da802ee6d24e04c098bcabc0d4e4e7edf731de8f6de7f0fc2b5f5de51b8acde8535beb6862b85f527e29e885caaa54254f8ae6969c01e4c2197e243effd5cbbbb3b737ffedce7c9a55279b3f1a149ff896bc8bd1b9987ac53dec8e589f4776e3cf31bc996e34b0e45e189f427eeffec45e079f158349ec45c599c4fa43f917e26e6cbfbc48b5fb9cc97f69c01ffdfeef3c9f3ce3dd2edcc6f64ef2424f4dedf4e949dc8d27b477ee3f9e1ac8b475c29cc95379e47179735d472e58dab7dba68c495379de27f1239df8743de45bbb96f4c169574c8ff861b8d2244e0f5d72be6b447ba11716f220ef76f7b371caeae75375d8dd74811f82fe23e8ecdad76a3f1e2716d0d47febc3b5f5bc3d7d644d46e254af58ebff8cd97d7f9dcb7119325c8e0ea0b3b172e5ab0b0abababa35b3027a7fcc5defb8a4ea2eeff4c7b377b377b377bffb5e8bdfbe8419948cf3d5e51cf23bb55230fc3dcfec6616dbc819dcc7aafe2d4abce58f58ea596e3f5e9c1c399e10cf98d425a89abf99d49cf34dbf8f1d383879328fc78f57741e7c626224e04dbd9e1e4ab8b668ddefb8f1e6423d371a5b04f9923bf5ad21fbbf97bef3a7a908dace34ae1af6fa4daffca7c56c38dc69ba66fa3d1abf529bd722e1ad5727ca99c8bc61c5f2ad5e6a2510d59dc53fa3aed1b93499fc6ba445ae97dda37ce3ec7d58cf79dab259739be540ad1f3c389fcc673adfeca8fe3bef7ac1cf9f58c46cf161c711c313782c308b3115c23b44654f5de6fbe7c99871f4590e9f93279572722aef78fbcf3ba8823e99fe4fbc8e5894662ef4e0e7bef670f86882084a0deebafbc917845a3cfbd7d226f24daf9956d9d62f5c4b93c4fd31fc5cd97d7a5e8fdad77b3d7205af030c4a9c4a102f61228d57b07ba48ca8d59a02bd5a7de7b55982d03c43d88a3e7edbd9b209cc050c1a4c81539bd7773abcdac8c00b7a4b1f76e1ec9242183d2aac398defbef4d0641e9088623a27aef40af2581364999226750effd2a8028a3cc426ba842f4de7f934235c1614a8aa4b7f76e55e50e8b0a910f0d477def4e6af5de518148802b8573d1a8862cfa631a8b3b2eadeff86b4f8b50c3bbd570fa6edbbb93e7ffca4fa4130aefeeeefc3c6b64d13eeffc947187cfddc1a87faebc93c9255ed4bbcbfdf12b6f36618ecfcbb9b34fe9897745e1afefe3f9f58a4aa23fdd4ea15c4c8a301a3ddb9bf0ee7f1237ddddffb6f0ce68f46c77df46433b2a6f1b595c41ccf190df88fcc69c6f594c8af834f28613fe650de19d7d9fbf84f02fffb2c6a9fcb6f2e7f69df01b91c519af8fc57f54deb6f227718f1789b827de49fcee2ec791458fe74ffc23eda2f2eb9df7315fdeb33f7163d1e7bf3e914aff8de79d9fc6fb4e9ec8bb58dfc4dceee4cb897971efce857d79f2f84fbca8f437f2b4dd4e19776b6bca7f07a3a8fccb1a45afe795e7dddd892cea1571e427fe0c2ebcbeb4ad3c3beb8fa7fc474fc44f3c1bc5e39de34b3a4716cfaff4caa24f9e697d3fefcc50ff1cffc8b3cf7827cffc897fbca84c1e6f3ac3997d9ff8cffbe613bfbb3beb4fa497f869177334bcb8a7fcc45cc52974c3ef3ef286bbab8b77a7ed760aefea6211594ccb8dc5e58c7f65f22bebe217d61f5fdec8130daf8dbf882cfa1c79d34863dc29acbff2c48ba7f2562aff89f4271afe4fe57de6f4572aefdac6f365f2ac91de682c2a3ffeebfae37e4e9353889023717a4fbb9dc2bc889f7d483fdfc6f984533177e2f4777811ffc85fe244de388d62528430bf91c89bc51d777e628e8617cf7c993cbfb148dcbb73e47d24e645e58d67f4a7571697c933778b13c1f79167fef1943e179ecadbf679d12bf3b398f4e79df2d7c6e272049bf0ee537a3d9ffbba381412f122b248e214e637b2c68b27fe8f379d2f6fa4ef332693228a49112c54088b497f0acf3cdce7b63d77e27d1f85b098f473a7f027fb36ec79255e34feb6e1104f983ceb5fdf34f29ff29b08b6b9b3feb947fedcd3c89b3ebf72997fb95388872c2a73fcbc83ed7c7977faf3cfbf98178d1ea9fc39df3f7d51efac3f7e27f9fed9f7f38879714f58ff8f9f7d46a3bfcff3b46f7f9638ebfff11cbdf7aade7bd630477ebd9b2f5f0ef16487b5357cf88f5e69df7c79dd8713edf873bc6fae83f22661347ab66f9cbdd1e8d9d6d6b4d6d6d6d686c89b1ee2bf1e227f5d1789c362d20f93f8da9a6dfcf828e532f748bc227ed645a3d743ba9d21eafd56fe1767617a471a913f899f1139239ec1bdf74f238bf6afcfb27aaf3fbe24d27e410f62e8a4a00731202a9e7d79f2286340d33b314ffe9acfa7e8bd3be8410c53fade6db4955fefc6903cefeef0620a0c71bd2bcffce3dd70bdf7063d8801eefdeea6bbfbf4d293c090759747dfc6117767be9c8bca1c2fd667adf47b5faef7ced5831732f5dee95b993cf3e57daa68415777c14def49356d4de4c9a292c6f18ab8dbaa267264f1be3fe4cb24d22d5f2691c5146d355d34cecbb8b5350ba37aeff92722dd2c0cea5df8f1de3b580f5a10c0c2dd4ddc2bfa24dafff6791e6b9f2ce677f67da2dd2d73dadf9d2258fc6f0f8dbe982b7d12c7bd91ed1bffb787c3fafbffede1c7fdfce14387ff49b87540dec53ca9e27f7b88bc8bf8d248238bc962fe8f778ab535babd1b59a1810a892a00e9bdbff56005b8f7cea0072b6c98d08315b4da5098d31ea49052009b90678293091ebc7a70c259ef42e15dfef1a1f21b6ba427fe21f2be3bcf3c9fafadd5d5c5b63b7ce9e93fd3bfbee9b37379f45f79a6675a06dce8eff23f478754719723fff11b93c7e2d97997213d537177a6b19d152a08f17be9897cffa4f9bed2eff9f38f388578fef1cebc384c313cf1e289e3de480f3ff2861bcab8abefa2f2c71d97c722b298626d8ded0e46df8cbc4f7c7917f5909e989f4abce4d1e7f8f7390624ee8968e73fe922d227dd4e611e1d604238861ae5e920c4108ea1981a766422d7113d632661a1b16c9d0aabc3f0e78a03646472126b53b0d40d400955b720c79e1b60733208d263238c6c8818198db8482934739d8ad3202e55e2c4a04d69748cf5e9b1299bef485a324ab9b522c7a53d79cee6707aa02c0eb13d13339afa8c18f0c413d99740b6f492b42296c6468c2d54d39a5286204c9a41f6098dd70b3d6e605ed8895263e041039326d9f9d46622cb9a2613b21324044860a40559f5e628a2cf20a1280b938d3661395278ba72c7026fd581a58bd0263d4ebaea0f2f26a5e07a130a1525316c168e22827169a4380f619e884a553601d00a33562cac011a61a64268a2830a002b3a2ec50e74411a42a46b8f1f23abcaf5c5475329b3e01aa3b111f243d619a845f2dd490f26313825baa2e4401934a07392a5466c51d0cb0fd26b74ea00ad1a8d44645962e24c88ad080d12d05153d1022a8d9902c4d1458f30434796026b4b8a437a5431c327d259f7a29466d1a2470967d09f2f0bf7a5b36f01c1a0664d16187ac2746100880b213cfad895174d742b1e068ab4d457a1baf4fe95e7a6135b0c016adc581367d524aef72d4e785c00ca812289292621aaf210bd8f11600802087af824a5a9a192054eeffd2b14df9d4759eed81890a1841a3d322278c5c22a89e24317c7c8d27b27a33378422046173a630a08e5c17c0c0165e504994da19486355c7f0869f8d292a6ccc74c8409d81b3616c508353090221e5ca400f59d91da14f14823bc324f0a1f1b862cb95416a80a264438807c882a8580f0451205127bacdc9509f2e00d1b3656c82f3506544948c28948161a33b93d593eb888b320696051211ee9a3082a1d9a80430c064b6a12b87aa044c212d598840a886929445719506e54205bc0110d68673f2650c4f8d8fa125d5973a6fdf2403a01018f602740572916702236c950244e715e67c8f8d82009d162d25d130f11ea1cf1e0f4b02c8220cab28540dffb88c7585ea80bec979c3b654c502b94428841d96242ef125796e149e6e7831d02c215a89701e1d121d0e80f88740921c27aef5dbdf7de7befbdf7de7b0ff6de7befbdf7de7bef9d6bcc1712048c64e82468410a050b648a16b03a466aa04a1cdac4e7cc2b80e9c50005c836cc71f0e4c20f261d55ac82f87020c4c1c5a1cb4a0fd149c09253125ec32d4783af3d89ae0a293973a436301697a20aa31563385498289246cd572a2e74ed7f84fe969f3449ec1c38d834c453562647cb1d095f22c83a8d7142a801516ebc7cc953a0c51041d0371ada233269e96549f04a8e9d1a5bb3a3fa4a8b8d923d43fa6854b5a48678c591e2012bc9fcc08d0658dc09c2b6772982861c0bdc9271ce88492ac3e60b94ba48c10894602853699922f9e7ad0991313666bce2340d953bae2c7e4c476c305590142636440759aa37606e7d040b2e3599458e8250cc0ca243a841922c19f4f094694a90d75580a221779e4169d65a68ef0b180578c22eadc880a2003c734e84d8138549dc9f37c1542d0010dd980b47cc71ca86da63edf2b083c896a1025ba4df151c88e84ce011f6e668c4dc276397a21f6d8c1a6c6ac091842c605c973a9510db32fb7ac025a60b8b07771ae40902002d79a24d123e99c47c7103810dd75502a5dc209151a71f2a4589abcb22d911d9b3e1e5cee778509265ad4b8e0729f4bc4e9001f3c34040a2cfcceccb1f0827a100aeb01004e2c18a08ec9816c8475986ac1e6d65125082a8ff2133d1d7b6852b919738053455e92bd1790c4a8142814a604c1a743905c60e8b290768b97a24c01c2c6439179c872d021cd0d1698f8929788e575cc423c94a1b1c14baed72f819e2a83203fb994921810da13d47d0c842b88161953686808b47785c4cc471409419134a291ebead0820a085c0c36099c3d153b404c498822f3e01de38514c06096aa0abc9da405283b1af0412b0d02eb184d02ed8ec185445d096120e555e11453335a8206cdd92b4d05e9e14f061b585c0f1d12901bcf8302a222702d203052176ac9b0b0f56553e1022b361cbac52fb24d4a33287f054e881d586a28a1752829d519f5a244ae1868c77f39ad1436c0f18356e5035075a2c48e9abf2a20d85a0015981e4749015efb8d141c6849066812e078e54e19175b9d3529ac1c29cc1c009b1330712e041894128b6196c34d56913a551d005942ac92567059c8890aa21c54526a6eb2665e78d80072c59d182ea1465e7c52becc828d591a3335a7e28906380160dee85844d10809072a84a914973c81ef8d8f39d1e847261c9804a755c21042a5a6ae48a0be10134de4c961a3fe0a831c08d0707da7e48508b6143cc44924903dc99f90c8851ba3cc841a54fdcb18599061819539d79194320c8e8206a226614759923f2e181be3597caf2102a3c8851d424059106dc190814283cc80a930a31a22780407e448db4dbcb0e4c81411e2ea5d9a803a4ef09033e05148d219b23c4cf0c27aa41498aa840864e35b1e302200f21831e62494d47049184ccb06108acc3565c2ea7f8a8d48c02aa608020ec089902af2e4d320171b1e6124d4f9fb06ce2bae0809045abeae6ce6c9a2fcc16ea0b4d984b4d52a5fa6c6cf41011e48aee81b1188428b4d9910a048544139a542a8149b80507188a0007a9862cf3ac29a5a6e901195f67403e953d49559c8bb7c4a52e09414520e08b5a2538155b982c2d3d3622ada5d95201a6112749588cb8595dcd79f3539ff1e2d264813c46438c8b48983066e973144558a077020fa1014c7081291aa2a73bb0226869cf26396d6e7e14c05d82d2a84399f39a2087465e5d8c107014b6c0e90a51009e188d0d41ae34a889f3635010a9c459aa13f40163ada0080d9707050e260c073149d4b5c5532a4d82627cf54931a80137795a07617cccecb864a5666c872fd1c542013fb9a084140e3c0197780264a50f1e294fb4e45d18ce3907daf04d6c3f528df2c0574450187a8978290a93060ad5a92078334446969083c20f0a704299b9e900f1e9d10216760fccb44d42ad49e496d964699411ab0d0010a8d88100a7e000880b1a5a201892a00b87b203d6350e303df9770227f2b33e0c4e3c80cfb8220f5891f017c111a1b283081c84abb39acccd8601841c050d741cd198cbb008c871e8c8034f0e2e8861731f6a8b80eb0e20255fda68d21b804f439a5fe9c818981e50195783813267a89541e72e4d12d91c10b8493904f511f9d861a306c731b8e8d434707fb66482b4c3451a0f3f2a28fa6a00848c2d3e507c49e4082543224953a0312e4b5a05141860c2c4ce9459a7b14b555b95e21e1742f1117be1ca90f2c5b505fc1ae6819454e3e151270440995a044014b608a20a48d4e711031768a01ee8c085630dca968d4267e7822c1672c2c0fe91245dae78252aa29e39d03a8e9120e3c3a61a0cee4450914907015c7e12785962958861b082dddafaa4a686e2000b65d0f048a4c1845c1d2fbb3b82526568d4c2c6b438c815e1d51023a38054019562ac39a2f7ccc96d9929826386e0e85223d5858f00320a3d3891255097a1228c300ea318a00a12ca0d02995b95d802601a474620d8d2814159658659153958c2878a0d6d1229b00911182d6286202de872314782a00b130f6294082ae29150810bbbe3545993624d0a2f3548a60238d1d922818f181f5afc84bb173b30d861f2ce1879e0e844932e8cd6308297f065c9c096a20d8443646e8307911e905063a7678532c14cc282493f9a4c513405c21d9a8d107d198e0f880d06ada52282c54187087d88280a9804610497903d7468e021a480021d1f33c2c82893937286434de61be227d33d432097737d12a02fc5952379748fd458b892e85311303442c0b4546d22a10182234e5671c93c322b60ae142191e1d01a2c6098f074412284abc7253b4b3e8a2e0492847c40ce854385c028cc9f1e7c088426c8d889337968123d63984a9d41d40008a32fb40c18b2a9346b5c5c8d52584af020812554622e14ccd994c09714285aeef8b0e8e34f65e104578686de9887be3d6f50a5d1b19123c1060a2d1278b0f5c558f640004652c828b10027d4de0db4af20968529429ecc9192e64c06021670212a0294ac4da8c2c0644e0920064a821134cbad0e41a20c33543096f90884e8491b336e5e47be44d0e693b1c4850a411abce9e26291d8262605d4684ab4e044c696b3fd24e5820b88e10c6c429312850f9d4f3c42d8ab97960f1a1ac0cd9c1b1fce74ba8bdb7180d81ba9ba057416c8286a0043135f8d054d301961010c6ba420c932410ae7f341d29f2e241414a8a165cead05e30d6440032b76c2ce6cc8d1400b4d813e0f58dd71c009db0341063784677860a03a008cad4ad70d1a7b5dc144a40810f500aa2459578f4401c85e615e413e096c3819b0c0ccc596b86ee8858417740060e0925766495a526b92a9c0d38329792344782abcf5e1b0affef000b3074187132f385164244859b483888f53105d99006e005bd848c19063d090c69601ac06784021d2e9258d0e3e335c6e6cf1320017236099020803888c812f456f2f8ea8e85a6024abd463ee47a9ce0a1347358414123918c28526480812a0ba1e8a8e7c91b3409e01d880c5f0ca52c4a621d20a2113a382180a05b2550c459050a80028d9608734c022a5810b44272426207d3c0019ef0a11ef9482f3054893112f16a4400296e84be00382b501c67e582a03873c95a82d8c050a496af6cc6d0d4af6596135038c024b0c2d41c6c928f4556241aa4fa2cd8a396b9fd56dd2d22d02a240203e4ca12ba1f1a013624c86932a5b783e300c91f0e15e14688fc8169c1b5abceacc6988961a281874270a8daf0c624522a4716420700a52926709e74e15357e433095971815503e487518000a1c48716371bc3e28f2216a7071738410a906a874a980494c0913524c942cf082d433aaee0cd02605a9885589a0451e4bdf43950f3cb23a38e8a2e3478cda440ad28724bf06a7168a07619ce2276e34bc71a1041dd124cc82468782ed0f537236d4bca318c8502541961698ae11934ae185dd5989f25a63f239c1202e6c4e80b5108ede60e58b26fc3e8c31e922214c103d82c0be2490db6a60841f2e6bfc3cd83377c587993c09104291ee3940c88a0e214e1820925ef4b9314186aa030a865288324c4ebe8ca5102c8859c8c1a7832006983c90ab0246900067c05016482c57c00d6210308c6882e7ea052353c71324f2fc0805a92c7de0be433c41e140c40aa2befa930b2e06c89891d64060ca4b3b63a17004010218002c7e306636ce1f1f708a941962808cec86263b0facd5c164a74271838d248a17ae234eea505d805a80dd84382758a32160598146a5f0606907de1831192e91156a608c8f957b97c40f45000065cd6ce144852d549244986a1163111154639de6b080b3fbd26185eb0632e7caf3d0054d53990999c6cad8c0c44e6c815d316000634044c3de72d2c0a624f284195acf191b044b23787889ea42634191462e4cc151e71c4d787046582601b2840c1d9a1b970cb864a8011697b632f02913f159b329458dbd15425618f2e4851b5ac2e4c3960c8ffc2c60c152a5b7326281faac39dc89352ecc13b777e40851d0ce8e2c676836a17193e54f902b1b3259eafa248188da48430c4a457e18aaf42a411253b3440304941c5459a61168128092854a5046a72d7872f640783297c0cc0d1c93eeba96463ee6985233de60e8232616838c2fcc499e5376be2270ed94ce99e593992109a2fa4ea956de2090ca00e1a53d6a9b143c8880cc07105a3ed9d861a8d18a1c7340e581408a450a4046922e24f481e2a5515a0d5212de3213d88e0b6e3c3a0500a2e3fbb3f7c0471f5601330c096439546802143e3db420d03b44982a93ba49c0604f0652a87c8a321c43ae1b887498f159c121e7cb412acb030671f292e0c1194ac595c0191f3839556082349048d6a180c480437a4a5414f2acc812c8112df094a7b629099d833963acf070552962657d453a150a025d1143462459011bc1664281b12a7394338a64ded2acfc20e32a24668a8b1d327f20a8021bc6402e5c1bee306954c90519d3851d25557e6c21f1d5860d1baf61273013cdaa8ea88d201aab690a2fdc681ab3330602d5a05365946c193027141f019f9a905a0126151b0348fabe8447a5214768f4f6be6ca5ea32a42625029c2c4048a45881e2b504001d8a14c4c1b14527f770c3401d4649fa0c7060a2449dd06b00bb48160c29e7d0204a7b22e642435f8407640238f3d6e0d29506b0a8479c9660414d281e95595264c57c2629004d510650f3c2300311a24f795ee0a819eb0126ce031b6235dc250e50124b320c256dd91a00918ba0103b61451d68617d8b36c928b430c05d6194581068e4460222b9060c5de993e62a6cd092126b1630b3f407afcb9b0ca2518960052a480459bd41481dcf99312530bd75a1ede8644502c802612146c100206c92a3b0178ba6ab2917b4864d5ce58d2c33c02404196229eb831a12514b200ec4882caf3c509377fa171abd111b40592419c5cd07842c72683a92512e4c9803b4a10f9c2a92caf4d86e045800e71e208c1d08e8343062d1030f21543b7940055120419fa2d7d39e3d1016300181f77902d3e108a2b8043675e050e6608d8ab816c82d0566fed4a28a7c4e70ed0994c6c9dba60098142152896d91d0000c475b230812f03ab5e26cf201d2892f83c860d879e57a30707821b25142e54a9c102e175e7e887b48a559f235face9f1cdd17271821ec98daebf3b19f43c45fa506c6c6a8c14f71a2b02880888814238059b86c614a75624a9d073b42a1ce02b98d68b3bf3e1db20f34909021f2005e05c3c9023c22a4124e5262a576688c9a8304947b461c6dedc8018708010f951438555c8467a44d1f2f4731b62801c5e02e879c3b72228ef58e082c563e6a9ae09b2b1b42d982f4cc1160eb8bc78c5466c4e03387be3b3632bd1f5e314e3d3a01d405d71460f1666e522133313567506e1e98e8dc7038f085c84002b4e01952311240e3242fb3a98b88692401395d8b44a91135e6662b51918a18d381bd0d625ca1b2809234c9908a3bbdcb07b150a8208a4440a834b8ad3c29e2b140508f20625d330051a223a79507b9e20c5a41aa7417a6f7c391da85247d6e94885d582f86a324143233144127f490951976c212a0a220783049ca550c436542eac489557831208008203492930794822f674a0d585c960845bc91e0b58194a7064b0850b2a6081225a941171e1c39e07007080344055d40541e46155a4030610446dbdc1ddb8e1c0a960510421396bd20f3801711a61bc6809b3b2b212ae61a45062c85afc0c6a182e61329b4088080b539447616603431f1204345f1c516c8ba209944b494e7f74fb31cad5d09249710440dea3061e477b05f7f44d469f20276a684ab46a8077d8eccb11e82c6e0fd197b208d9406ca162decead05b487c89a8c305660655116bef756106e43c3f0d847d5d09c000a05b9d15308f97213c75e8e1986c1e81441043a2edcda237be1a72dead31e72465931379983e1571c0a784d7962faf06c896b457ae28a1c99b3300c2220511aad0f5276095092c6342ceca60d9f2b325a24c0218a44865e81a8412197fe49d5e4dfe97468a60b490036702509e2ef225b150748251e21e1066f28b8005246131071349a121434e8a8c39b2b34873a3ec04f07270aa0d14180f2c9849b8d825088cb2d89c290052410b7a086ab404121716cb2e98e768e4880218411534f43970d58100d0d5e44a9d5da61509e48582402cc1c34b9cb0365562c87cb9e0e27960860f1777803404b7ec601a63e950a40e2c6e6ced31ea71a3eb6bf56207c98f223e47170b2c40484ba2b008072821138088153a4ca04181c284111512663404b038409097468a18cd28132323e9c11533b71ea21f5862c2ec81332810990610828829a053c2654f14395319860039b455f6e54e490602dee2440026ceda80bdc0d0030ca65a28727087c21166815ec3919e2b304fa5eee013140f2c22a41542090b3858649b6c9c28f4a68c93933c7d02a571204c112e360b90773e3da88b23e7d1ef668bd881cd2a8ce8a921daa0810c04a00273cc18b23549c70771feb4a1589401fdd8f98ac2d5f6c70cadee0e5201764a3b7754e0cabe0c99655e1a05886120e340398a8e162f63477c400822e3dda591f240451b4e461a6c1d4a55cca1b8b3824465dc1a54d7c6edebed1497b33218012306607d91c854615184471e143c60010fa8e391c822445e340dda325ec0b0c7c24a8b3bf5b2081ede1aa0571eb53475693f90a17238c954478496aa0870a92b62f8269d208091a82f699c2490b78057461839a2c4cc1905c4f4084bc97cbcda803c49132a5fc090a3c5c61a1cb00f6251bcb81965a341a707c6ec41b487788541f0ca1027a5dedceca48c99812107a4f9aceb4e037b7024c020e30180368d0c1770080883a0c20261c0a852de4a2140b6064d86613036f48b107708c084ccc5034c36852dea32810255263b5884b06668ee364316b9b01733507d515302af4e1aa323bf3c0b4f021a080c88bb0b1391a66fae0c025b5a3c1efc09c07da083833d0d32157aa382e90b13087642dc31c3e7411cb92f7b8607fc38b6ec8140a114e540240643dc0d3768747411f180851d2a619480a1d1c2490ab93f2a2e0c00214d4d082e0256b46d6af3084c8f489de6cae23308368312001aea2982661864885c92d66835a8a22287221d8e7c34ea435107cc8a96054e465490b1a24c0a0de5900069c87eec51d1661019d68beda1830d064264702965081ac436268ba56125b98f093c02b006a148902804409109afa343205e30213536880029888ac8d9500c72c88f83b47db9806704b064e75565c2a72f8f32e8a8c3f605a48a102d1baa9898924ca6ac0cd4783266c10ed14daf879dbf608d01210c3118b525854a8f344c5b613b307482e052103d71bc4eed80d457471932e0c901760b98354c1170384a74680e1c3786cc107bf05991a0cb9650c1a50b86a0105029cd5c2181eb0b96579f92482892d811963722bcd8875410113bf4400072ce986031e48b5c83039254a011c4232a0b1b0792b86bf27c72835bb814ba46b1212289d8098827d529821024db18313752a63136342f36f88c1488090a01109c530cf23440294bc6236c517a5c9931d065035209335e61540290c1ba32d89303eb2e61543a76356d4db8ad6ac26dd5ba0b947ef461a59fe743295daa55a19a0ad1b4b4af57cc8bc7a232f9d9cc32c149a67afdcb3389c9241b935cf47e7ae8709e6b77cabb33c77d52aff6aa9d88d4eecb8581d574294942efbf8dc68b29fe27b17a22eeb66a5d5b1ff7eee4b036de44fc2b897491da9e168484a6e34a6131e9873ff77a4565723854f16e23a418777a7977b5b1a8fcc9decdaedecd07bd9b5cbd77f391a9130f59a495f799213feb9fc6879ec4fc9ecf4e1f7cb4e851a24781649c1cce1ebd09127cf4e4118e33f727dab9a6fc39edf93ef1c48b7c680cf70f6caa96c151ad9e1a369ce9a9f759219af66be5c7d38cc963f1b6916e24e8d6d64c158d3acde14a738e6d6d4d5d971f943f0e1f2a7f92cf1f713ff3f9e3870e7cfea8aecb0e7cfec8c8aef7ff952496b9cf6f1be9d6e7e31641585465f46bb85268f44145422186fc1b8d46cf626ded86c3bf0be5ad54ca0c0625aabb5e8f11051a2a7f5e1cd23f89abd15f0f511bbccfc7fd6f0ff13e445987e80e19c293a51b5a508881aaaaaa2a20f0f7334d060e80118006f0a84ac2a3eaed4ac3696285ea11fcc2c3ca6a01d605453daab4a455600bba8cb932c2a3ea2dc197d618841f3c12b8a87a012c53905f2dc82291055f6901397835e291f58647578c1e095ea0000f2480c4c32ad843cc108f05390503782480d0e087c5c14acd0a0a112f055f01f8a842c2c3c104071380695b6a5910b652f9b0da90055b7d003a0076c802c52355bbd2ba22c303a8e04aac0a4d410a34ab1654a16991b0da80e5e24aec4aeb8ac682ae055d092024f8c272013ce5e34a0c88c687951e1e1668541dcfa105b2aab5050eaeb6aa4c601a90ad2a6e01d8d50f8b0556042c382baf06e69502a055d5022010f8036201b38009b4c6aa805875585c0ee2b4b6b0aeb04c2056155ba55665668501c77a6449b01ac3a301991e5b575511b2da7c5439f2416178f654a82c25422428d01e3d4bbcb9264ce411203cbc3792d326350c41075958305ea8804bf5a7d020407f242c41a74809d103707408501e3a6ece8051135e0060992fc195a13c7562b440a104d8ecd03123c68b162ae04a90905006f625274e8a336674e418928ddd48090d1c3150e3c823152a5110c48001f6b575280f9e3a74dcb449b3444991e10c172c54c0b901fa83c7cf9c29261629029427013a067ce97224c80f4f9d0e2d40c700390400e1f16b37798b41274a931ced49200e9c35033449f2ebe18c183058b8c91cd6326c3100f467cf952a4d80fc7ac609371232874e6912a03f0ae08903674d9a019c280122e3849b070e6b2f9b66e0daaaeaa9284d6240813d7812880327cd004e9a28490224d250460c106a4253a5de503868d0688c0992232d58a05b921ce9e32b93201510d5ef9455a95f045963aac4fc06a80263aabfb40a35850ad2801b80e79784df9819014b0c186642a80233bf123ce0e20206ab1c2c68106420b6204cc198d65602acac2bacdfd5959515d0ec574022e08195e06a0c0bb6d202922da0ebf1e15785967576a59505572d7060562db81a56ed011900b7aa10fd0880c755831f56822a340664b0191e5a760bea2ab4063d7e6c04abba2af3aa831d1e1ededab67efcaab4aade3288e2f1a88759b5c1100f0d817a98566b5b1242f4f8558901d392e6840f561b16e0717525210e889783114180257a0ce1119680c582ae055d36487825b3702d087874c81394785bd3e32a021e774b825809800cacf2aa8a20e99155d5e08acc74b0f6b0b4452b83212059a3a434f829b00ab1851557a1f830f0f05843d3e381d5991609ad0b5731aa3460c53ef008592b2080123eacaa2cd1fa0aa24018d06f46eef050b0b4abfc60a03a30ebc8801cb0c2dc1e5a58f195565603e0eb560248a645e24aab0185363cb4b0dc18cc6599882093d04a6062c15cdda1001e605568635d575a576b0f185c69299029ccf8c2432bcb44d94cf6cb72a1c00a18b432af565c695971b8d2f061848265020c5926aeb4b0c218b42530ab824b00d6357641ec4b4316da0b0cca05e2f1cb22035eb8d2ca1262ad0cd6d72d0e6b41159698d51ad04db0a00ac319022b019656959a158a2a388469150138c1ea0b4d904503639903842cadaf046f8f052c104e019b7556655665762586878606556d561bacca1298558d394881643e7808d54ca00d4cab025adda030e309038d6ee41933810e4073464c17233b72f0e179f6a7ef66c478d1c2ae6e2eeeed848935783dbb60a1c259852781549c345902f1c10205b130ac2c7efa80f9b24206b146e6b540c1d51d3970ca8401d3858b962c57eace30a809ab04403797ca14a8f905a74d7486f796932545fb03072dac304d98bc0912211a20cc5aec827529d0cac21a8be0a64d0c437cf0dc41a04e9b25499018f9b50ab8136e24e43536cdc0b5d5804195004a8a0ce17ad4ce396346cc0e1676af0ba1ab4a50a04f761f3cb809b0aed2088143c016009e1cb98145000196a880fbc001c302b3545504bf51559dce4dbf4b55967e90ac1e998a7e877e86122cb0a0d04fd00fd04fce2f0df0cc2faf8a8669664ac822b31a6310f603fb79995d630facb87e5b66b0aac14f8195d62f4156d60f0bcbea5765024df3aa6b75313f4070a510595426afc6fa800af64173d6c8e56d9f69e79d0c773dd154187745e35a8e2c0ef98ab5570e95393e3cde4afba783cf1f7f2f501b5984f3701bd28a2651880ae7b078aef4493918500cf3f9e19481c2b097731c054a75e271d7b18e58fd87aaaa5779303b5616b3b6da32b1ba03d3eacb6c6066a9556f0742a92ac3fa65655d01ad3ad84fd87ffda7c05c5105c2ec581cac3a10b8a03bb832bb0906b4b2627f0a7edd344de0cf846962993fd3045ed9f8993f13ebf725679a572656ff990c84c0dfd5d599d66956fd7e554cccaa2a2d33d4cf4cd0adcc9ff5334d93c6cf347f5975bf083ff37755f53333fdccdf0a13cc3481bfacba5f03d30456014df3679a573f93cd0a88d5c02b0b685e317fe655d50f38347f5559a69980fd2d303f64995666400f26d6ef579502b57e3f338303d7bc51952a2051a5e2d7ad4454f59f17b30a785665f53381dd2a8a19c9ecbf1e0c06bbd9cd2af3676e32b37e1a4cf367f5fb014d7335d5cc2c73c1eff7fb5db19966e6eff7abfa992810cd341584655d30197089fda49856bf30b3ca049afd17f73bb5ccaafe2b5ef5f3ce4c02ec1310307b083c3fa079655661990c4ce094df2f0bb632e1ab5fd5f95b90e002b04a0bad4a25a0e64feb2aebb766fe7e8b805766825f1550cfa4f05b9a2630cd4ca0e067666199bf10bfb21f1970acaa03bbe9c0bcfafd4c06554160d52fecf7fb995766956d263033155805b04abfeaccdaff136577b57d0bbf6bbbfe9a66eb8f27d7b0fbfa5aa5bf9f3f1dfb6fcbb3faaa2ffae7d75e637a55ff976defe19a76e355fdb5bc7e77a5d9d74ee7d8bf755dd3f2faae71eb536faf6accbe703dcbe9eb5f7b6d53dfd2e8fbb6b597d9788e679f7af4a7ed57d9fa4e76513475e137456dfb6dd3991eca735dc3ec1cc3afbbdae967e919b2fb5798b6693b6ddf7576575a865cb3f4cb7a34b56d1fcfb64ac790633745ed798ed9f6dbee9bd230645bb553155dd3f9b32d1bdbf30b79453dacd6f54aabb51bbbf5ec42a66738f6b5eab26bdcce2d3db7903d5ab32abcc66f4cff5ffb99858cb2eef7ffa7efeaaa2edbce76b23dc3b06bbf9e4d5d0fd7ec5c27a3a97fe9954d6b77765f569de9e4989e55b54d55fbbbaaedd67e4eae539a76e754f5f6eaaaacebe564745ddd38fd73fcd9d4a559bf42a6ebdfd6e85cbb6bcbb63ff57172bdd22b8dda6f6ad36f6bd7b10af95d5b1a46dbdfd66c1cbf750c27ab68bdb2bf8655f6a7360dc729e499a5fddcb6746db7aeedbaf19b0cabe95cc7ae3bcf6c9cd66d8c424ed5d4ae55d5aed3754d3f1b9f90d518ae611bfdb11abfb6cbc66e321ab3eccbda2c4dd3739caeb189d4bba8fbd357ad6d182e21a3703cc3ff75e99886d5f68549c83f46ed1a9ebfedff9aa22b2ccbabcbcea8676bd5d7703aa3709bbcae1fa6674fa3ea4761db7ed964d745d5d99e57946dd314f6ef085975e11476d7da7dd3b7a5db5784fc6d7a9e5915763f4cbbb3ed8690db184dd9397ddb9a56eddabb2064b8feee8cd2bea55f745d61f7831cbf9f5d63dbff5faf30ddba1ee4b9fdeaaaa66c1aafec1aa36e079956df945551b5667fbbbabfe520b7286dff9fda1e46614fd7ed9abcda35aafaf5fb786e7deadb584e6958fd2fbab628eabeecdbc2f24bb3edfbaeb46bb7a9edd9f69567fbcff5ea7d8baeadcab2ad2bc329daa2a9afe1b8467f8ab6ad3cd3353cc773cdbe350ca768cbca6dfa7ab5b6e1cfce2afc6b76955bffd1b57e57dfaef5ff33abca36abbadff628ecb2b65f5b369559f7b3afdabaae1dbf6fcab2a8fc613a8d6bf8a7e9fcceaecaaac934eda229ccb66f0ddbf69bb242398dd93a76ed3676bdaa7e944d93e796a667f84d51afd229fc5734195e511786bf4dfb759e55bf6e9061177ee33a5ee7b89dd1d65d35c8efbcd6f4cbfad77d67d7af6b06798ddf9fbe1e66d199a53fac9ec96e8bd63f8dd7b9b667d776550c72eaaaaeddaaed77ddf97d5df582fcc2b64bb3e85cb7aa05798d6b98addfefc2ecfadbda4ca65f3a5dd3f4f5e87fd3b7826cdb2c8db6f487db36765d9a82fcae9eb6d9d785d7f8fbfa9d20b72f3abfad8baab6f72d8aa6a7dca6f1db7ed6566bcf7ed79620cf709a7ad47d6db4a5e3992e936738fe9fa6d9b6addfd87b32d9b56bd656e7d6db2cfbd5784cfef51bdb360bd3aff77e7e23c835fcb236fd5777fe360abb28f14d5f33ba126624cce8deccf0376d87680af762d8261636068175c9c0cabebc72be7f763d08e352fa1cc7b0858705d55bcc818606650cd2e1c70b5b90e2630ad2e2f178d3675a2a85045958576456e9f013c7aa3400d59f59a12f30a385259379c4fd9e104258ff478facd2ca2247414fe00923119d8867ff9a9e61bc70d182855d1ddd9cdc8a381570297a37519827dc20b529304729f87951b957fbaf9c5977a9ea0dd78649dcc49e89e296891619ceb513ed5c3b2b41a9c34bfadbe1c5a408bc98f4784863f25824812989d3eddd6c789f11a9e2d7770a8fd7b43c27cb464fdf6f48b7b9bebc685777c3296b3f0279176b3f824d0e27e6cae208fbbe9571b51ff18d3e9ffbca65be2461347ab6bebc3824e2de8489a377f346efa68dbede4dbedecd3dbd2babfc469e5ef9bd526edbb66ddb9665599665599665d7755dd7755dd75555555555555555d3344dd3344dd3145551155551155551155551155551358ee3388ee3388e61188661188661f87ddff77ddff77d5dd7755dd7755db76ddbb66ddbb66d59966559966559765dd7755dd7755d55555555555555354dd3344dd31445511445511445d3388ee3388ee33886611886611886e1f77ddff77ddff7755dd7755dd775ddb66ddbb66ddbb665599665599665d9755dd7755dd77555555555555555d5344dd3344dd3144d63f8755b7655d3a35b77b1b526f789b90aa3ff73ca3819f20bb7b9619bdca6820d4e1ecaa990939387f25b5d9b8a14c316225278bca6bf12af7feef17cf25626dff43e1ef2367ec0f37432f2e7dd74f0f2c3f1e76e1de86f87e3ddc6daed238b7848996d0bae90e1ac9bfdfc4a7fd7bb3934678479cffae6218efb24aca67ddadb39d2238bc7a292c68bf511f77be76da2775346efddf4b96fc38779f78713ad83cf7d6d8722e953fcafa41be186e149dce7bead4df66eaefd650d9f5461b28d608542e116bbb5723241981f4c0f260773ad76fbc93ebabe8f821e267175dd5a356cad67198442e1d9d97bafebfd37d765b9aead3452ef66bb856a52811af3e20cabe91077d5b4b4e4965aa4deebeacf5724315c5bab3f5fd13e95b76d0e9338dbbbb9aa3b20f5f91424939e2c3bc8dc90d0814cbeeed0d34349810a4c32ec18b1b602e923119c1d0c34449854b584898e437492c6022915e9d82246c589103022e938364221a63f01ea78277520d297567400c74ec55444a1c9f1078a9e181990bce598b27785668207ad1cb493bc03c4a470c8a11647c666e269140e520cecdee0a0f2e0b8a335060f021a601c4cb28ae401a28b00870b302348401802010eb3207f04b07135bcd1294f419a52f0db0d386d8e7e6c4842e2461060a55f110f836ec401cad4ae49597443ac923c41948b19362805a98a805662878d34af8ba382f1dbb871c4c81d5ce6b6412201071e692c90d8d8ca62ca364226d9c788cda3b5672345df1726583838d1e3a14fc9e04d2d4c96b4af43a4073bf2d5471f5684117d624060f20dc2d1264fccc0e3bbf284881a31a8173ebc01d632d07d7ac3a73aa06742d00ea93d546e21584b02c4d8d3b34605014d3ad0ec3d99bbaccc512fec0d817469a57e24f7ce00e91d51b3f242ef53a20d5b90e4f0d2a353c7110d1b3e8e5e113c4a9bf4c0a9e9d1cd95b5c8f20189de39424799375486969880a7227529e069b906102ab8bdac03963cbe7c054e8e78cb135c0a398a3ba360e9a5a2d10e9c4e871cd28234aa9d54743ea6440f4c82a608902f07fda9cb6d40c8452c6b3482404f57ae80d318002cbe08c10db59034a64fae0ad7f2a80d853c7920dc18c09c2a255d0fdc8d840682a1e707ffc125c1710c1528fd9cb8912982748c520a724f1128a08a28673a585ad82028a44c43a5ac82c5d35c0d51a8ec7c8525615403019565c0c621493594b48173aa898bbc1a2d9ca8a522baec6a540863a14e6fb495e484df5558280d240bc870c10b013174492067a6362b44d89271c93004e4f0d01363c73dc90a007e8e5f7d6e3439e41a665c1e6e1b73169bab1f506e39c29a863c2b7a773012944d7272a02117554dc811817784f462c59294523d76206b9c9073640a8f0a928398b2354db9047935218d1d2376161c05258f56e8bee2389154e9680f3066cf0815998eea04bbb52a7805e3a95f0526a3debcc63c788c4e98c00900ce23c828638d0c04942cf061c480800e57818736efd38fcf8cbbd69067401e91ba1da99a17276f6e6f0aa8333b348fc6a54c261c2489160d359cd8b4b74b168d1d944e132eae701a6e34ba8a6646e8b15b507c44778498a5cd5051cce988819f354fc5dc12844387e617c5106f1e91210377a6a85507cf46991369880be0052935678117442d12049851b59920e611db4c59680a4039443489106291b5f700af4f83324f60cc32e101508380179ac88ae079504ace543851080f0d4e0e4a566d426c084a49c144e30a6cdb991d6d8a8386949d8446aa49c31e4b6c1644fab421e0cf029b022143a39628f0f0374930e0850e3effdf847daf94d4a5ffa3049020a83c9a3e1c2242707478d787506689886d095b2d698ea847ee02b73e03800b5bce23bbba4fee9243d5f0d56d7510ad15958188854ef4c8aec58810a8e82e6af84854c153e9e49227796782d4a1d776c8c994b600a2add4302fa64b5ee64073150a65a928f542f35f43890a903ec09cfa527a088952c32ef078d6d8d096c430e49be869e780c8c1e76380f217ec14b2709b42e0ee81560f880b29bfbb397743e0c8088760487327e7c5ee0816395077640bb090f9b8716068a991a3bc04434f4335609438ada9c130d481840b041999944313491f7d93e4c018ce7064a2eecb128b1d3306b812c35700579f19b9555ddadd22433342cc4890e57815c973c928c48305cc466c29238b4d264d86993b326878786422caa4a30c96858cce27390805de174301476298b8e460c488b2c3441c118b70c4f06a96287fdcd0181b52c0800018e3529bd3038c3a564eccdda92405069234b60238682181610783c20b4d8a662b186902bc70e315e1c561a6e3850ff5458a17605b34dde9ca02c2452417665cf0b960abe3a241db02510b2e2dee16715e8baa146bf0550627168190b0a0f28845cd83056a17815d0227d01278a0a0b26b620783cbce421f541e5a440dea3c25f9d41b1e80a88b5327448e13a33b085bea562cfaf4a586c4a53aba4a8c202049818a4147805b190b5010c8419783ae04f1089bf8cd021d179c0432f224cbe61a41b09c11a9a1e7c0a898c317cc7d3083c4cc03becc5c96991c2132392c72453956aeafd8636305143d2bee8e2b34c030db58d5dd880b1577409c91615c5ddc042a2a32a968f3a5a2878a37157091e0c82c80dbf30037a2866390e2508a2d299029e0502980cae210db37790344b6f492b412e16dca1bfda68682148a3b0f50301900c50b1128ccfe9ce87402ce89202756446890808e3a80136e9470b73431dc70b891707b33c1a8cfc4176f4269c984072f26b04a0c2a214009a2a4122b095424f490780246624822032619947c805ce427d3b5f2794403dad9af491ba636366d3cda50b4b141621bc3a6c726826dc1084323b68c388a41d9d27b1ff19ad0bbd47b1ff1cb323c7bef22fcf8f922a41c022282e60a244208844788513442204024c49210215c843003d1a9c7582aed25329f97bd537fd403f5033a9a9e8549e7d193fdaec7f5101ded670ff6de1df4069d415fd01574ad043dab63f5ab6ed5ab3ab0ffbad9fb8ff84bf0039aa9cc08cc32ddf4d2cd2c5b1298f8affc39f829f8fd4a99a2b6ca18755c29341a97c8b23b656b7aef775b65507a99de5619b1affd645f5ed6b655b6a696a1f5de339c6b6868675b6509ca3af2b834def4797e6471497f25ee8fc79b2e7a65eef16ea572897fe46dcc60238b696a1b4ee57dc279b833bff1cece9f179145fbc47f7e2f6b9cbeefe7652091a2e832461a8aa2e832c6d969bc7fd2d3f8bfa3904306daddda1dfd69ccf789c4959e840a39cb011990df38dcfbc8b3a1d213e9215e14dac6ffe04a82d8015b6220cc14eddd54ebdd149a695c5b61343aae14de451b594cb3914578890a4f34e4ddaffa5658bfcbcab4056147fff59baabf56fee312f993fef8e99feccbf73eb2885c6e818dea5b608cfaddb90526e7eecc91c53b4beffdee547ebc9f238b5b604bb6c084dcddf9de4d0ca685b9144cb6be40295f6c7d2189b94faaf5e5b8dc79c4fbf849cc7dd2245b5bebdd1cf3aab7baec5450bec999d0adf46c752dd87ac0b7c505616beb4717a210d668c16962c321c7072945de75f9117c800007a59d157a99c603548b14714445120448e0c79c3c56444cfc993be0861a451084a110e4a6c6aa239602bd15800c374675750033234927a01163820d31f826a84510448350fae0c20a524e210a68eec490e809b3d441cba91869392270a02a85cd045288ae37a7e06ab739a866166e1739063d7af0c92944716ba87e04be513ff24301d86242e64f6a9a48c9da00ea804174483478f08255648891f311b29381d7404c823875901422072431b91b606b929c728296b94d840c30be604b66831f5798e25a40552cbce09c698b400eef0320380e3e03844b666bb610488f084d2a88bb28a126a59ad12942a00e3d4214fb1a48d539712ac4878fd8570814cf869367d30fc00512311db6dcd8eaf32948263d583c618204c903dd29b83d633724742093af63638066a938c249f9971e4a0a5460722649ce0fcf54143a641ab8d656207d2482a3624f1521b49854278890698830a96a09931d61461db262e0b73100a3933416489928159c3b325569305939c42546c58910302051f1953a01eb10c9d147d6088598fee4b704a8200c8b15065194804dea40a42fab78b19205e90dc72131881e8c9d8aa9883ae3060d961817295c9c1e9441d1132303923717615d4419cd3241900061ef0acd040f091e418a4ba013199147610f9ce41d20268583580a45efa713842ad9901047c666e2699486af0c9fca18907048431803bb3738283c44b6e29c51d2b1c3115d97d6183c0868808d8a4c9d6ea6d86f6cc1ac227980e848753a838aa1f5654aba61604690803004821494c210b011634ca5395d41fe0860e36a461737250045f9e2944981609e8234a560146d485d8110c34787d302626d8e7e6c48227b5292fc8d92a4aa44c10b58e957c48320250a92949d38942a135c1050a6764d8a2244093ca459a08f4a11056295e409a25cccb8b054f3c1e92c54182eaf205511d04ae808a2010991ce9ca802c26cbd2e8e0ac6ffd67938a93820e5e7221c31720797411ab0490f7b16b6e427e12b01071e692c8ef079204660ae84972a6f5b594cd946c8dc8660f8a20323101c1ec6d83c5a7b36de8c6c00d4604717569af08b09160e4ef478e06007e2439fa0d51c03b818bca985c9829a6a4bf5930b04d553c6223dd891af3d9e6899b2e50d920aac17601146f489018139c25219382e969446135c38dae4891978c4025828c70a38b3cb42f684881a31a8971c069db108ea28325bc10658cb40f7c90d13b78f464f95ae0610e07240cf84a01d5221d6dc1c90054730ceafcc2d046b490018544421c1c343b3d4e40a84352a0868d271c6cc904b104d1534e53a1ecc5d56e6a8172c84a910c0ee470526b504205d5aa91f490e300800a093a0413cb44000a47744cd8a8b3d5f677c0780591480d883441bb620c9d19585012179ba1037135e12ea38a261c3c309e054971a012497212884e151daa4074e18a9fea1c4ebc29d15ae6baeac45960f472ec5153fecc9b94b6a0447e828f386cad01c3ee4a3894800841231e0a9485d0a740a12c40bb28c9bf5d174014205b79775000d412b2cb098712cbb10fcf2153839da71148159c195a6d3a44c625c0a398a3ba38547831738202a2c1522e3a5a2d10e9c0e074171dc2881b88446765b0bd2a87652d10dd412000568c95b1aae08257a60123445801c611de05022a00bee87d7a03f75b90d0875171a0588123d2feed6b2462308f43cf8c1f662cb4534035393089cc60060f12588008332b0314b528bf1f1d542d2983eb9aa1d0d86dc21f38089c26feb511b0a79f240df4b5c44838aee923916c09c2a251d8f2d5a422d2d9da2fb52fc8a840682a1e707ce92a826426ab637745c7049701c432d2f428660046863e5891ed9891b992248c56c495259909aa5bd990386dc53040a28223d3e95e1f2e2070b3ca8b8e8606961838050950c2cd844fa91c19292a15256c1e249aec9223a458a571ea16a0386cace575892456022a5d2320185d89ff1406519b071289226365452ace9e04546a9ac0d9c534d5ce475d5d9f172006d13001c2038514b4574d50dc000cb000b1616da4c3d0863a14e6fb4459706014a5c64c802ed9180df5558289786f8209445f6486d8d5e58860b5e088899c35285d1958eb70720b8d375666ab3426444a52f3701f824ed0c8141b1ba40396240657c6b94ee24c4376018027278e889a143ca853b5bdebc703282baac00e0e7f8c5878c04209b0462ec740213cc21d730e3ea508480a20c2d75c4038488cd596cae7e4071f063d19e1f0511ac965784350d7956f494802461b197d80c5a8291a06c929363c392056cb0c810c264025c84aa093922f08e50942082ca94168fd6cdcb8a2529a57aecb88dc7297bb6740d5ab64ec839328547c5422716659011d6ba1411c494ad694a255cc42c44724ba06c93a4af0969ec18b1a3808d19206cb002513a72696b8d9296c39ffbce912b32b0285ad101ce18dd571c27922a61c97aa2f461d2230204be0618b367848a4c7155963d438acc2845ae09766b55700a50806a9e709930dbe8c87e15988c7af3cec51256b44954258b1030b68110784825c0288d003009d88854691017133801c07904e1f004a3001a7ad8045992ad91818092053ebc0f34180c30e0c4611318023a5c051e5ab13f452441506061cf91073f3e33ee5ac30d44481524ee15c80465341838b5e34accf000343a0e41f0e1160642dd8e54cd8bb3c38f8b545c36cc42421eec4d017566a746a546579f067a4cbd41122cd411f2a5d3dbd99a4d0f4e24aae2290e4226130e9224011c0d2d1cdc5d38ea3078c0894d7bbb60e999f068418b4668004909944e132eae948900f579a444b846035c1890304236f296747df1c0a386011010f9d0553433428fdd82201054fd45aa0d59d420dd1162963643f89529dd441f0a9594889d8e18f859ebf4c6d4055bd1a3f7d0892e4b100e1d9a5fa401104482170f34321c826f1e91210367c60803812889e9d3a9c81eb13a7836ca9c40c3400ad7a1ed86985e032fbc20a5e62cf002c58b2ea78051ccdc41b425418019559b890e267e9cb2b24707f828c257e03db5c6202d0421b53b3a53ae56af4c59680a4039c402cf810b31f55115045b4408b1c8dae32223a72c636649d89526630dca3c8131c35426ce0d8e133a96cc3ebe6a10f042131911235f48a11ca628d9b45ba0949ca97082d016096907a8953963226a0c4e0e4a566d4102a591fc8479315843e7ab94144c34aebc89eaf8f050464c0d199f6b76b4290e1a52374c169519911769cac868a49a34ecb164433b01802c8a34ac390546a44f1b02fe2ca0c0c4d2183590e220708a8b90a1514b1474b8846b2a12630d006e5d26c180173af83caea94c182874158cac4108fb5e29294b4b3a1971804dcf47a1520f4a000982caa309bfb080112b330005f79421220447873fc854a34ecc130526590a03a1cc1211db12360a75b6ead8b2dc4880f3608ea847ee82960d9e88addf71f8064502002e6c398fea3829a80247c3c4141b43c2e42e3954cdde03f7991a9bb2f495c2d2f5b2e59fece0818104090c0b298473a5205a2b2a035180335cb8e0203bd4424559d823bb1623429ca234080b9aa92307e890d2a5868f4415bc1448981d2cd0db8fca155cf224ef4c903aae032a990b740ec11821b6434ea6b4e58f0126563ca8a13eb0e18a971ae6c574c9cbbf0233e280504a8a9c08869a1b723a81c40d92400c6086022d59112894a5a2d409adf9f1730d285795105e6b285101d20790d1624280eed3082f3f5b3280d808d487e4b81307d05e06738c56043d8444a96115983a9242e16951cbc0ded71a1bda921886e82369866078dd49436a4b4f3b07440e3e155b8e6c45da0ba4e46e0c2f22e453ea130157c6a80844670d98f965a79085db949d502738e10cec0c644cae560f880b293f190b2089a1f566821d1e1905166b679eaecc9c4890127340cb4201181c19e1100c69223c8045486195e1a8039717bb2358e4407dc120e008dc121d6f28d9022c643e6e347572e2463eb103edb304d6c8515e8261a70396431a5a52b92825e10118254e6b6a3074199436594a7543edad0c0917083232d50840cd108a057c46a1280321e9a36f929c1790e830b025c9252d018b07495a611c0067c45251823bc08791a3230926eabe2cb1d0d1840a7212decb162955d89518be02b8f88029f3409c9c132b6ca67459d5a5dd2d3224c10018f872c497a832c1aa36b31438c6bc4d71cba0a18aecc4535896e35524cfd57561d13238ea5ed1a38b070b988d58ce2853a43b0c9e2011c1066d326932cc202089d3252b8706f0d0458979786422ca2423964a212278208caf62d6852ac5150371769556f889a2c401b1483f827c928350e07c6f44d46853b230c1c615732486894beec50144366b7147c010b1f3da61228e884537fc9c4c4ae15b341db9b6d42c51feb8a91d4a11624f0615158c2c23006129480af376c4cc9f0661746061dbf4058c71a9cde9a1c311440a0a40450990a72d2b27e6ee5492427379d0010a9f4c0bea8ca5b115c041ebc8200182a4c171c4800d405f46ac4051e3c0a501a597851e351066105c5939d85a98f2a18c23f5e02a5499087f4cd16c0523edca8f3575de28a060c6490730af082f0e331d3f51e65dd6a5236150ae3d13a450b2b4ca283f958e420c9d17c88e800915a647c510140e2e916ad8513904b645d39dae2c207640a0c0181b0c7c371e705d9a531413d6c71e667d8992c3a38751990835077a4bca24a72cf074c44d97fc0904d68297514a4f171e69bd3084f683c9f2410c4c99d2ce0ec8540979666b8ecb604359a92d3e132258325c26a15011a5828936087a14e1c9c26aa8d026ae0308313eb2bc0f084582796488a3228105a652c5a9a26628a18022a83e228137e40de11f45aa0c55989fc0a4b734b81c8962809b08ee284de94ab1065f656c6aa0c31e1f3cde52d983e0870ca1eca439e2c01e52b7204b9a2038083be580d3954b8e2231b2f3f66301994f6f5d0ae017c01cd871094d9524d150273b5c825a42bd796066cf1d80c385037c0754692366458e8b376b1ee4fc3c41c04b492755c1eb045a020f94538b0e0d406440c11a818fadac0b580605f97c919870a6d9b2a4549508213cf4f901b735c783e9a781aa30274ebcfaa0f2d0226a809eaaa2e00cd4b51cb195e4536f78f0218994331dc4420465fc8960690c21be32057058a08406419f4aa6727cc971627407418ba94c64739c78080280025b8b3e7da92161e914914952c2a4ef9805c69e9419795303571371a483f778800c912f461090a440c5b0e0c7d387c054d2e9c8e556c60214047064528001c89d3f0a3e19211b6166cb102cc23606bc68599344426e0f8c78844dfc66014869450a3d6c042d72c20527818c3c49322b6ef20f6b1b1ab88d20836039235233436951c0a90c0ce41801041b25e3529918708d1ee439150742cd027a9006e6008b1c0a26392094ce7d0d2090a8cc0c12330f783259c2565cbab28491710984235ce89cc1555053228703f6c5ad93f2c196067e0188c6961865783241b03e299190e72041061779503c8a52ecca5af066521805e5118e9938018fbd216c31973c645d11941820f49467cd0f9ab1cdd3069e14fc52c0a7451da39a56914e1bae36dcb50993e3684c9db66f52a30178b4191ae3a7539794303b56431e4c2736f597ab82020c413e485b08d419e3dc667d462d52d14182180cb38d559d8d0474682901ca09843bd5cb2b8051556df33ba5c7950c1165c2ea0419f07ae25e89302cc35a81e1cd31c38a99e2b4a253a919a96e08411e49a496e3f49262611106b51dea1eb8c5fde80105786cc96e2205a13325d4114f990b299664d911b6d009b4402a1814dc3c40321449077ee82b421f305b54bcd860d56bc65cfa19fab6ce18d2d4a06b4b06961f08c82900d31028636f471eb815c0669e3268509e258dbcc0f9e2b26559084a930865964b230ee6460fd7580f518dd8c0b8b168a261ce99233cf158044524516205619044d88bd2403564277911bf93bf4e97c006e4f9b75ea53dddb2ab8daaf17c49666b54f574eded97653f9dc296e4375dd5b6a5ed564de3f94dd34a32ebc62f4afbb9756dd5ae614af26cbf6cabc628eaebd59d5794f8a6c33e65fea6499fa21eb2ea0f0d7092fcc2734ba7ab0bc3331bbb764d7c53e40dab299b192a7f6372496716f766f60528498ed1f7ff768d59964de3f8d72930120cafa9eaae28bba628daaa6decbe705bc3ac8ac2b5da022145003e0a033a62a011a345404589862710d1a12104a0a1058613808586138084861b808304e12730500420a0e118f08f9f31601f3e5e4005f62c00ea916d38455fba9d69f7756796ade301807964b44d5197a6e37665dbb5855b3cf2afd7d955df1865d1341d2620bfab0baf73fcdaa99dc669bade91d93985dbf865fffff5ebff8ca64304e4794e6dda8d5bdb7d617b65fd9ada91bda7eb0fd7b0dac26cebe1e11a601df9fd281bb3764a7f7b4ee3b97edf000f9051f58ddd1a46eb597dd1394d492055a8b10af762f8e303a423bf711bab6f0cc7f6bbae369b1a20b7685ab3f36aab31ccaa3fade7c86bccb6f63ac7a8edaef367e161edc200ca9161f765fff72eabce6a3ddb9ff8a6ac0b601cd986fd1bafe9a75f3a85e1d755e1c870cb7e7ba5ed368ed318b655780ab00019fe701da3ab3bc3f00aa76dfabe91fdfbc6f3fa7adaff799dbd97037423fbfa9d6975f568bd7a7b9ded763d00d846aee714a66d549de75fcf2afd6b201b99ae51156ed9f77dd1cfb6e94a7cd3364e28dc025c8303a84666d9f9f5f38baa745c7f7a6e8104c436ddc26cfbcef447d3fa7bb817c31b9846b667fac7adcaaedfc3291dd76674255ab4702f8643201af9bfef57d7d7add51a6d6736fd368ab8dace009e91d3379ddd959debd5b55d3465ef7b3623d7e95bcf6eec7f4d7bf745816564b77e5d357553b55653cf7a380e908c4cfbd8a6e93665e9f6afafafdd00c7c8690cafbeb5d73465ff8aa24331b2aaaa74bbbe6eeaa2294cab339edfb46a0307906336ae5b974eebfabbb5eda66d1a46b659da55e3f97de1556d53b7351a3dadaa4e8060e4145dfddbdaab4da3ea5cab2cf14ddf24c02fb25da3ea6cbf2b8ada7ea5e799f8a6b78d2c7a595b15756d6fd72aedbfffc437ddf3ca16d8458e63bb653f5bd7f05f5f7856896f8aa66674255c571502e422d3ffade75fb7349aa62c5a7ff514708b5cc768dcfa9a46dd8fdab357e78442a1d04ded00b5c87efd7f8d3feb5df6c76fbac2648059e4f5db76fddaeb3acf76fc69144dc700b1c8705bafdf4ee774a5e71ab55da000f25bfbd94ed114a661d8b3714d7cd3373ec02b72bace71edaa280caf2babda30f14d5d2b358055e4d45d69157ee715666bd5865fb74de700039069186de3d45e6196ae69d5a53d314d3f8df4398e746bd3392015b565dd988563b56d635655df74756d155deb975dd7afd629fd02a7c8b4fbd670dda269dcaa29dbd2c4373dceb108b500a014398e5bfadbb047bfedfe755e896f3a2cd6b57fe51a0c1000d9c369bac6eecba6ef1aab2e3d4cdbd70c308a0cc7ebffad6fd3d666d7f7fd693a4c5b150f108a3cabea1bff16565196565bba1da668aa07f8444ed38fa6ac3aab36ece779b65b01804ee4d6f5718daa2e5c7b754dff8f87e9156013198e5d1446d5396659d7f6edec95399f6cc66666c666c66626824dd50099c8b1caa675fa61b4b5db5fd7f312397dd3348d5d167d7fea7ed62b91e7effad45dddd6a6ed74052671eb358eed76b6db3d201239555dd6d7ff7f3a56ebba858fc8a98f3fcab66c0dbfbe85632372eb6b775ee39a4ddf975e5b5706b088bcb671bdb23e4651b87561741ea6f9c7537a3aa1106ef179061291d139b567755dd53a66d737a6db1738449e69d4ae6b1875df977e53f613df94758142e476b669f4add9d86e5996ae7f10978ed11fa7df85dbb985896f4abba90b448ed797ad7f8bd268fbaef00aaf69db027fb436fdd2315abbed3adbb0ad07f491da710dffd8a3ac676b4fa451b8857b31e403f6a8d317fe289ad6efecb6765bb7702f8679401e724da32cccbe288dc2f18bda682a198ddbd4655fda453fec63d54525ff75a6dd1a56eb5fc7b48fdb53f247519476dfff6b777dd79826be691bff4cad331cafeffafa148de73836fe4ac9f6ec57d4bb713ac37e4557b88d405d591af628fad634fbbe69aab2ca3cf97fa4643aa6ed36a669f6b57d6ac7ea30fe7951c9a2cd8ff68afe46c92c9cd2a84bd33f8efd3b7f1ac44f94ecceeebcbe2e9aaeb4ebd2e94b7cd314c50f02b94e5d5a655f75ae53b445d154554ffc42c9e84fe39fce2e9bd26c3daf2cabf6f981925fdb665d3ab5e1f7d3be46eb4ff21bcfaa8bce6a9cda30ada2fe01778e5b14ad59746e634fb27fd9b57dd5768de17686d3b7938ca2ac5fdd96f52b6cc35ecd8b7b6cdf47f1a6b1891fa7b44655f6abb10ad32a1bcfeea6f655617aa6dfd6bbef1aa3b003323cb7714aa36f6bd770eceb56d38ada36ddfedafb9f49465f8fc671fcae36fad3d4a7985e1475d1975557967dd3974d5d157d59ffe7d6f5290ba7713c13df1459f4c7dc2e2a6b366d4046e76fdb68cda2aff72e8cc2f99fcb3fae5e92e3b44d67f7853decdaac675118cfcf92ecc6f4bbc26d3cafaaddc6ae4d7cd3b492ecba73cbb66ffb7ed5d36a8b2c1259954d29c9e8ecda30edba36fddf188edb25bf49f27f59749ddff4b553da9dd35b59b3667425cc92b7b24d284cdeca5a28c493771cff24c9ef9cce6eaadaab8aaeecfacec4378de9dedc95cc2f926cdb76edda2baab2a8dac2e8170a93fef842925dba65ddf5b557d7bdf9f2a5bade7cf992bdf37b24a3715dd3b09ddab347e9d5fb21caaa0bfc1cc92f8aba31eba2313dbfe83ab70cc8351dbbf05fe3575d558faeaa5098e3d7484659387d63f49ddf7a8669d425be699abe702f86c71f23796e55dbf5b19fd3148661ffd5157de15e0c8bbf45725bd7efcccefe5d6794465154286c5db817c3193f45f20bb7edfad6283dabaf3b7f3a91ecb6315bd7b1cb7eefff7f6144b25ac334fbbe6becfd8cceef7b48feb10dcf71ea3dccfaf7c32df14d8974ee9534ee95b9f2c687e9eae2674856d7d4abb08da6343dc3754c2f20b774dbae6eebbe68ed69ef63e29b0e8dbe38c7297e8564b7fd2e0cd3b45fe7759d5f42b20da329faa2b09fdbd9a65995f8a6433657deb89ac68364b84ddb9a855bba46d339feb620d946dff9f5739cc2b61abbee4a7c871fbf6f22adfe02cd6d4caf2b8bdaec57553fdb80d4f7755d967d63d49d7dbdfa3faffdbe685aa7a8b75db875fdc431fb616fd7b3bab634fd95c315e98bf53fa6697eeb15936d1f1fb76b1bb7e867e1d68edd9ae8b2347e7ce4747553746559fb7d5b9af631bc648f4be49a1f6d183f05e4b6a57f6db3769cc2706ac3f01e196561d66659d77de197a65d578f8cfe76a55b146d591445e735cd23a3280bd72a0ba73e7ddb7ac623a3a94f3ffad675add676cca22a9b801cd371cac6738cd6e89afe96be23cf2eedd7f58ddb79fd6ffb6104e4f7fd1e756b54fd7f46d5d98eacd66efba6afabbe29fca22a5b47ae6db7aed53456d5559d557f805c7fd4af5e6dd3d68edbb5457f74e4d555573ba6df7475ed165ee1113f03e415a65f1a55e9f9ad57dbb77e8e3cabaffd3fdc7ad8bb6d5bbf257e7264949e59b8f5a9a7d9daaee91495336333435798b199a171666c66a80a33363314ce8ccd0c4d61c66686be99b199a128cce2deccd0d4983c1687ca8f4416dd39bf38b2a77fbbbeed3aff344de33875336373d596cd0f8e5ca3ee0bb330ea57b665e9944d53571df32b404e55d5a3f4b7611b46dbffaa683c4c8ffcdec8688bd22a4ad7bf7d67dbb3ae919f1b19ae6db8f56dacd630bdd629dd464ed7d8756b15b5e9d5665b356623cf6becd62afcfa5aadd9d9bb6b641fbfb55bbbb4adaeedcad22b1dfa53a3ba6d9cc66b1bc3eefaae6d9bae6f4ab7b6cdbe6eecedd9d36acc778f100aebcf77cfb831792cb6567e04c86c8cb66dadaaf3dbfab9fd699be49746f6704ca7683dffda5d55daa368e4d7657f9dc63f7de9344d57575d972c92df19d96e63b86dd11fabdfab2fcdc26aaa0ef999916d989d5bd4765d36557f5bff32b26f5598566737a56114fd9e7597b44f51213f32f25fdf7576698fa25ea561a3d10f5fd58cae84d9d075e15e0cf97e63e4784559d67de9b4b563b48dd5017e6264566d55f5dbae4aa77e56d394f8a603b0f88591d3b5555b986e51d7c7a97f57e29bb25c7e6052d7ae5d9bad5bb5fe2a3c7ce9fcbec828cbba2b9b7eb5b5db3fc335f14deb616dbce9218cbb877e5e64f4d3b64bbfeef7e9fb69d713dfb4edf2eb22bbe9fa5fd8ad55dac7b05f57e29bba5e7e5c64ffc6eb0ac76b0cbb2eebe996f8a6eb96df967646e3d94661d466d99a6d896f9a77dfc4e137d6c521f25655b011f3220ba35184199b99199b59cef773e4cd66c66666c6666646f76626b368daa6707e5a647aa6513fb7ff753febda3fcfe76c875f1699555f96a5dfafd6de45699bc622a768ece19af6f4ea63556d6901647466eb157d51b66657d8ade72bf2f7318dc29e465db68dd1da5b91d198b63f4abba8cda2ff8ddd2a72ebdaab1bdbf67767efd3d40d407ed1d6c3708cba714b45f6f44bc3af3b7f34856bcfb253e4996d67d655d5ef59dbf6f12a4586edffaea9da7a785e5777550190e997add516b6e1776dd3d6a351dcd74e5514467f3ac768fdda4221eac2bd182a7f5050375e5b585d51b845d91765d1559d63359eebd846573a8653146f39ff36a1303fda4261ecc2bd18def83d91d5b665e3946dd70fb77fb5692732fbff9ab6ef6fbd5fdbd4db4d64bb5e55167d69f6a72c4ad32a871f13b9fe6e1dbf765dafa8773d5a2f91d73fa31f76e3356ee94fc74a643ba6d734465337f5ac7f93c8decf9e465bdb5d5fd7466124f2a7d59876bdedaa696cd3297d44b65dd7bb336ab7b41aa3703d1b91d79fbaeadbd62c5aa7f4878bc8aceb6bb6a5d7d8dbf5675b4f44ae57faf52d4afb19fdf39cc243e49fc6f13bc37e4d6314adbf1039855775a6e7d4b7ee67bd7783c82acba22a9ab2705da7ed4a0391d9d47bdad3abed6314b6df1fb25dfbcfd6ef9b7e14fea9dbfa90d91746bd5f3ffb531b4ded1eb28f5b1b4d53ffce6ababaffc743fee8bbdaedeabe350dc370fbf1569883994af66eeabe709aa2306cc729cda292630fdbb4adda2cfdce2bcbaea7648fa66b5cdb689bae2e5a7b53b21acf1e7e3dcdd63e86ddb694fcd6313dbbf09cfe765555558d407ed1d98ddb757653f47567352525bbae575b1656d5597d594fcc8b7b6e6af46baac21c25bbeefb6b9766e9afb668ddaaaa6ac71425d3ae6ac7bff5ad7761b7aedb341dc2312190e754b563ba7ed1d8bf6a8caa396e4642ce4ca8a6a88a86926d56ad63d785e15fcf2e3d839251f6b3f64cbf6a0c7f36466be36d0ffb94b99b50981773b73b47ba0985ca9f1787778e2c0a85f4d743d34ff2b7e99a4657358569b4fe694b7c53e2b046fae1f0ae53f5158144baa1ea0bf301b9fd1efd2b6abfb00bfff6fd9ee4effe184de7f58ded574d7d27199deb3465e9174dd935fd71cd494edb744dfdfcd17686e7587d37c9b64da728fcce360baff3fb5b076477f6dfaedf15a65d5aad5356933cfb35b56b96fdebfcb6eacf24c733ead6693bb3b53aaf33bc629255987e63f4b55916456b5b551b903fbca26c0d7bb87de3194ed14beafaba70daa6a89ac2e91bb7ecbb7e369efd87df588d5fd84e6de29beedd4abc21112f228b766a49865fd7d774adfaf9fd1ea6eb4a72bcd2f6fb59757ddd79f5b5a7247f97f6744c7f177ddf5665db4972edba316bd7706dd7af67ed94f8a6b19b2b6f9c6d479892e43945d3985ddb4fcfa9fb5b37921cb32b5bb72c3aabe8eceb9f429269d766edb86dd9b66e5d1b7e24a7705bbf70bddaedeadf584d1dc92ebcb61ef5749ad66c1da728f14dd9b44f992365a40a3336b31a594461c666c6662614be874c06e478b561d746e7d5557fda7abb91ccaef04bb3acaba233aba628cd4886eb54b5d3164569f46ddd3f2f92d1155d537bfedfa369bac2ab22d98d533b4dbd6dfb165de13625bee94f24d7fe6d535a7d555bb5e3d89d11c9f1aaae6babbab08daede5e5b2214f390aca669fd5dfbbbdf9e53df7e43b2add21f9ebd3aafbf66dd0f2f20a32cbaae74dc7ed4b369bdb685e4d94e67efb6f3cca6ebb7d994906ca728eadf3a5edf7786e37590ecd6706aab3ff6a867e7174597c912744ba3170a85c2f72f8d5eaf55deb98fdf26a620f9a7b13ac769fa6b1576dd19790ff76e251e6c4657c24c84199b19ddb7dbf37271fa06926d9585693b8659374de759fd80e41586d53a6ddd0fb331ebe3d48ccdac7633a37b33130a85693a4c69bc78645ddc5d5339e61fb98de79fc2ae9bbab15ba3df8fec631a6d57bfb6b3dac2bfaefbc8f55abfe887d7948ed9da4d633e725ccfee5ad3ab8bd22ccdaaa902726dc7b45bbb30eae9fadb34bd474e5bb5566156b5d399f6b13aeb91e31486517b8569fba3f3eb7d1ed94ee31766dbda7d6bafa6738c477ed1daa7ae1da72c9ab6734b2397abbac44c4086fdf7e99ac2691da7ab6bd3cb3bb2faebb89ed7977657344d631501d99e59b88ed537a5e1d76651637299bf76e415a55df88dd5f6fdab4fe93a726ddb31cdbafffd9fb5d903e4b445dbd97ee1ba8d6b16855d3af2fbd2aa9bbebf55d7f7cfdf13dfb41d621a20bbb0aa7eb5f6ff5d51fa8d5be2fbce9163f4af28edb6f14f571faf33f14dfb7e9edaf7f3d6a44fd1c39423c7f33ccfafbb7a56b5d33aaee3c82bdbce5e6d633865514fb3351cb9757d1dc7f44ab3760d7b142e404661355d61dbaed5da7687b827ba69fd3f3e7c5333ba1242e1afdbf78dfc57bfbeaf0babedfc63d44d896f6afc6a2aa2c69f8b7fadb6702f86b0e946feb13dc32fdad2b05f5b7aae896f5ae3e36d647b5d51d8a6e33a4d7ffddb97f8a6497f1cd6c8ffca261bd9bfe84f6d9a75d538b659b425be29f236def4708dbcfa79addb955de7189dd59825be69cb66aa61d5747d5db44d537685613665dd9886d9344de3f9b5d935a5e94f7cd361deffc9b7681220bba9ffaafa67dfc6aefbaa7f55edcc34b2f7a9fdc2acea559a8edb14c643de4678858946fef4eb553a66df389d6dda5dcfc8f5bcba6b1dbbefbbba6c1a7be29b0e55b5ce3423bff3bcbe36cbbef53ac7b167cbc82ceab2b3edd3759d595a6d3df14df15ce993ac6bb3294c3232ebd6ea3aaf71fdc6b0fa6b9bf8a6467f2cd2bf1e23bfed3ab3b6adceb377d5b7fdc43755519862649785dfd445d515555f584561e29bdaf71f2aef01e4f9ad574fc3b38763d86df9fefa668691e3d4c7eedab2f19f5f766d67e29b0e919ec6c1c82becbe74ede9fa85bf1dbf34f14d871f57fa649aa6ea09f38b1cab703bafebfad26a0ccfff13dfb4c5953e4e5d33bdc8f1fcb66ecbceb37767db7d6de29b0ee1d48cae845cbe3c9a919033837b31348d985d64b8855fdb8563b8866178ae57e29ba2e9af55d5e422cf343bcfefecd539865b3aaeb7c8aa1a7bdb7b4fb375bcce68ac45867dadcef5ea55f4a3bffd6d16199e698ffa7a7e6d369dd3af12df94882f93c33ce3728ecde84a2855d41fb9a7027e53d784c0c4227fb89d57954d69d5b775ebb2c437b543e1a63a3105906dfa7f9fd6b58f53afceae4b7c533ce4d04d3f5e2ddbfebfb199d999b199a1306333cb97298442e5cf53faa3fde57e7dc3098542e16d349a4053f5d737926dd3b4857b31fc5931afc8b5ed7feb637855539a5567f6ad625a915bff53f6db1e9dfffba6ca1c1fe645bca8179bd195c88b78d1ae36fe2289e15028fc42619b50f86ba170d8c8ac227ff6c72b8afe744e5fbba567e29bc66b0072bcc6715aab6f1ca76ecd7e368da3985464dafeeb9cbaac8fd1948ddff88939456edbb48e5dbf7effd268abdac437550e87ab8abb299fec73df26144a915b1a9e511645ed9af6e90c7ba2195d09a110cfbb6f229a0220b72bccb6b41db3349aba33fa95b7521945765195aee1d9aff69cae768be6c8af27b3b012138afc7d5cfb9745dff57f9856e3f82d623e91ebb84d7dead3954dd13a4ef6e5f16d2385c2154e623a91d1387ee9d4b7ee3cb734ead2c437ed9b87aa6a23eb84c21a4605309bc86dfa53bfd6f3daa2693c7f96f8a6356c9c3d0cf762b86732916df7a7f4675dd57b1fa7314a7c53a150287ce349e612ef5597fe6d1cafb64da7e9fc954bfc36d243583dde4a9b557ea3d1b31d6f2509a130577e3de44d0b855f79d36daac75422bf6cccceac6fd1d4b56dd87be29ba242a1195b6a574ceeddc921cdbe41cc24b2fde9788e59366dd119b56b94f8a6a8badad51f0fcfe7c5a437de775241151389dcd26c0bab6b3cfb5fcfb11b13dfb44d8542612e140a8542a1599c9c9959dc9b99199b599f50684657c2accf2e2abf9d19dd9b99505827140a856c66eaba34a37b333321142a7fed3f5128a48b42d74e79db4323fdeba150981cc6ec9ac2bd186a79328fb4b01ad374fcbf3bfbd8a3c4375516d3f475611a715d947eeb8fbaf55acf291d13dff4c633fa211eb2882c1e87f8d055efcc2232fcb2f17feb36b5551fcf704c44b6ddf7f5766bc7e9acb6ac7787c8b00baf293cc3e9ecae2bdab2c4371db6a9195d09b3b93936331666746f66357229142a3f2e14c2702f86260f53884c7b16a6eb154555f88dd73526bea92bc50c12fbd49d593b86bfadae2aeb896f4af76636441687c95b59afad324fde2610d865d5ffa62ebca6ebbba22af14dcdea7769762c1271a19ed9fc2ecd3e8dbc51f6d348154812aed2fcc1a22c1bcfe81ac36afa57787984f3650a248916c693f7fa9537cdc2bd18c6993e64b7a5d336b65ff845ebfa7b96f8a6f9d15ecde84a98c942e1af85c25f2b3f8ed3a52cdc8b2109b3079baa28bbc6eafbbe2b9ac62a9a0a8f7a46a350b6611875bdafdfd9ab6a4cabc4377559983ce4b64655dba6d3dadbb00bab9ff8a6776e4657c2cc68f442a1f2b68542a1d00c559766c6e4b16846f7663674e3611aa7702f8666003b641885e7f4cfb61bc36afcbea80e996dbf9fd99a9edf95b557fbcd21fbf97b3fbf769ca2f4bbda2e0ed9665d0fbb9fb5d7b48d591a25be693dc44316f1e59aca6e891b324adbb5eab6a84ffdb7e9d5b621a7b04dc7b61ac36bddd27f9d6fe470d827c3718bd2e857edd7f52e8cda7c725cb3eaafd3368dd7b8b5fd9e6cb3693bcf6bdad22a1ac7ebd793dbffc66deadab63bdba9edd24b594d69377dbfbac674ec5918ce657a76551a866d0f7f169ed71797d195fd6d8daeffa5db3a8e95b28f5ff87b755ded577d5596ad21db29fae3d86d571585515b45896f8afcc621fdf58671da0efbf2a2d227611249d9b65d14b667d6bb69ea55dbbe65ba5e67d6db2eaac6b00b7f1b297f745deb949e6bf46dd9f6db4799fe6b3ac7b3cdd2f51aa7df36caa8ff6fdda2686bd3715d7b3b4ff668cbb6a89dbab49fd318b669c82bfce99af5b5abce6eedae765166e3188eed98ada8442e4ac6537114e4308a840c21ce4c4502005000e312403038281c8ec6c211d1903e6b3014000365aa6e8838170944812c87711404410cc3401001041043080106198f6cae04e77e4d80811c7d1fe40f7cc85ff6c3f37782dc0047833a10746645300de89382d487f9cb7ca4dc11c66d5529cc7ae340f2a5a12c6f2ad7ba45fd533110985591b8d18e8901b21d0d193d6eba46cd82c91a6d6aa245dc6b655bcae59e419ccc6d50602061af2f81bfb98fdcde2cdbab6b526899a39a5fe69516540e8def993f178482799cc71cec79deb7218fd60217d409440a16a555bd93ae9bd101fdc59fa2b70bf7a8e8edee04a317a3fb793bb247dd75bba3cd5f34c718f510686c8cd07c913056f476257307c2e866471ec37a4857f4b63f20a68a91eb10f30c8bde8e2feee80545317d4d3d8f1db97d742770151a972f511bea4576c5c804da9c93ab0ea3436dab8f2a42d49e74a451e04e9667c5456f072393de525e5e515df456b4201d26e4e8ca8bdeae38fb61f03b694e8716b930cd333ea2c801f9b778b42def75fe9b8de2fef8a643848c93d667ed9259b7a6b0d3de41a078692f6474b07263e44bea1576deb8ac62d49d9eeccaed6c11ecdee9e1be1b6200a5a722fb9798fe39ea75bdfa6fbb20bb16f68e8393d4828107efbbbaf4899368a22486f65991cffc8389618f6f0e4bad0b46895b19301314108ec7d59aff24001eab1bf80dc7121e43fe4365db026bd04e198fc576776a00ffed60af29eda7972bb02ff114e41d3c81219a83412f0e084dc3d119f00fd71736281b0fe393b8e89b7b5f41bf9332eed765a358c99387f8181ef7f5e0b25c92563a9f143f30385add0004ff4d226c168ef8e5fec8666fe6e330a47931ba227f1f863a8fd273caefd4eadab8d03bcc8cf3bc4f52981737621e9a07853f7ab6f01addbfa840e96fa2660af8f44921bcb8cea30023ca19a9308870abbb0462d3fa32efbcc54c14f5ab4a78f75d4a3fc2a64d4d5f27d0c71024f726557538e9fd339541c9c8cdac83dac1baf5676022f10ddf86eb514094942d20df63eb74b3f6ea209d970ad278c37a8258f19929c1131f55ea920df13aec87b57a543f1b8cefa5374441b5b082e45e26d7d31ff68efaecd9e7285552e01ca27f3008d702098dbcb03264abdf0cc23b289d0004ba9c1d02d628564e4218673525b5bd47107c3862e3ad3a815feb067b53f585f3aecfd843e629e19234051c450123da2122cbdf2039b0d36e87c5fd8538994465324ea84e28bcab96f140add54522b82b8c14172ce191d0895c5484981efe6f71298f75ba2634c88a5207ca22603c1ffbc34719ef2de1ead2477892773506fec083bd6c355cfcf25eb308075c235acc3a7ab1a0652459c03071d82e36c68be7bbe9bfa496b3725cc07cc26c0e7ba1f76d4cd14a046626fcb1f87f8bddc8fe2c94e80b1ebc8e57ab5caf6b2aac77f688e3edad8811bf2a81336a69b3a4edeacb21831e923ed035029879933df0d7ff8546ef40dec44f4fc6bc8e61705ccf91b12a9f65df69db4df9d6a3995b635452dc18f3119755bc74c12b011a724dc0b16155c5de714399959773473c7818ec730e991c800af0375f6b03861431e31bf1af1b1f3bfb4ba72369f4beb1a59bc2779aa13910d1c5cba09ae2205e685c2cfad7ea3bda14c14f60f7ded465e6f0cfbe5e2eb2f5cb094c62d55efe802751b4f7c8b0d359051498a4e091dea776e46fbf5c35d20da22bea0a6ccb4ee2d3dd1f7646975fec60d71957009ecb06ea4d4b504468024e2b9cb2bfac935de803b9c83f7dba59ff72b05c29184d71b86e08880dd4a04051287b66257931109b218c640f6a69f0d05009d46a261833a2a573c486f383d0912c3879e3c675c09dc8dbe11b102d3c4d2a17baae5b76f7817f749bb48c445b61971cecb21a065ab9debb7117c66f3248c533eac480c0f05f77b90de4b5ea9a6913b060a10f1071f46223ccb2567a453e79456c13deccdbbeef7079ca41f6d3440280bb2b73f1e813453236b584c068a656a127dd467400e2aa542ae2a0c0841351b3c77354d4552cb92b9a5a34c29bafd6b6e56af98f64044d18b57cce482f2eae55a589573b9730894ccc98d3af10d14c0610b4eecb21ceae77c48561c9218b523073bbec061dfd56ee448c065ced9f97a2ad53431b612e320965fe00511dbcdb51b6ca256cc91207d347467810cc2a9e447c42c4f41f80794194ced4d2ef92c3d9d58308c8268dc42d696fe091642a26574a54ed390495dbc46bf71d2be3b2b761743c5bf1d965c76ae6134bf5fef932bca600c24612919e020b450d4053860d8f0b37e9a235503786f43f64ac621ec483cec9722b638687699adf91cf75c201c6b62e78e66bcaf375f5424f6439d8ced43e220233797cd74e15bb3942834dfc62345f7a162f69856b938779c27064e48c1a93c916a8d1866e668345464cec00a16168841b51e49a57d5621b9a2ef47c6b35e750ef0462bea938e725e9271a7c980c8edf2f7180b4be2dd4a4c533405f8af226b63f08480e4effe818258cc2a87fd71fb9508e2914bd82a7ccb4839502668f302ddced92bee47ff65fc4cab327dd1d78ca0cac143037c032bbd4fc0d2e57258177ec93ffc89faf7eec0579d5f733f712f024d6659fe98e4c4100330806d8daedfba56fab0344b9ce344c4261e6cec911ad2729bfe6d4e28d2ad3ba8c2bf974184fa58ca1aa03152f41ab2c3e00bb24848bd5a949852e32a5c04a251fa7166e7a813bbffc9e6b210ab0f0345805d719502bbfc62bac014b81089cb6912bba6aa67e9f2e4d017754041de527ed94a9bf4196f0cd457c24c537fdc2cbeb00ed2af2109626e0ba95897df7b9c80e94e06b666df6a0922af82aaa49ee8af65cd673004dcb7db0f6479988a3b280988e7205a69b988797f45e1d076fe38c8ab64c39495eb211e1981d257a0d6a0a48a23891c649602927e8d0e26820adc5d3402a5b08bbeeb5b60939ade00b202a108d645fd354398f629d273636d8b146f514dd6cc2a7f9241330a3081289bec6a00741460a2cc1c69f0008ad6f1d6b3637e246d2445c2b2bb801ad21c732bc1d124ec549330197d6d4a9352a4332ce30053957b03643706d1638597d2c43602b361ef4f33627a235d547675b63907c5991a0f1c15ed886b4a69206c47be3f6ce5c2d2e0aaeef815219965aaf7bdd34e317d1542323fe2d93dba2b03b61d10b42a017e0158d1850ce8a40608c402f64a88a024ef70bfd11c1b278ff17158bf59356eb5e542c8e0697bd012bd3481562a30ce527b946908b0a42b78e00bd43e0482f30cc742ef02ff31fcc7cf6e3dca8fb01d77672fcd00f3b4b3a039f504f3aaa81b71115268f90983b09275c45f54251e8d1b172d8e5f157c0640d498f4f3e16b3d5e430650c24e41ff440864ed58c182afd59b0d1b736e8fe754f28881390067595981df2c08a1f02a1043aa615eda24c05f3132b67ff118b9145fb118ac0b963f1407dd0796d59cd5a157a59955a7c0dd393a58d47b1e37d353928a99cdd80d2dd197beb9c6b6377714a159e7853e28a59263c2c946c293e6c101c5ff5ded3ed149eab75c1145be8235de4fdeabb554a2d7bd1b3d21f556f2b5aa26edaa704746647d45e1f146e1f68126776e0e8a92f8171bbc2808b08605040533182f8eedddf3435c6e5070dbb24778dfcc80d3cc04eb982548340961c5a52981cd585e3542841fb13adda66a228284d45b996e329704aa269d44e08bce6435814bba7329c1f256ccbf5a8d3701247f4ca2e587ca1f2f677a14aba35515e5f3843b85b73d1d384a8a7ec1856f8c7bca1cb81e23610af12651e5c69bc3725ca3bb8d678af94287bf0d78da551badc8372b5e174c54a4b27bca4c4f563a93d5f21e4d5036869503d25c41db4ca06ab5525a87ecb19834d4bb0193663c271a301c686a109dcd7c56a8241e8593a200053edb8fdfdca02173fd1cc020cd76bf3dd5efb8bb0f616d1e8de2cf4feec1b5055c05d2c1b17fd013f43e87d1150f9f2df938cd4fc28d0a923c416c632e05154460200088324ecd5a44ed3aae40b5020edb0d681360682d15792758536276f27c6115428ce23d2c5f6e858231ec96608b549898bf23d0429615ca23e490049a50749ecc580286ba91e348e6130de8198231043ad02f9a0b322e7326a1cc2e14261bc5ed7e8157ad191e4ee38cad815ccbe16caeb3e53ab6c32dd5aae039841036dddb5f8b03ac44b09e88ada6f3b6bcea1c309b9235e1ed7a5c44403a67712e43031468a01bd6e50bd33d0bbe9564dcfb0218a505068b2f20e36bf8a7bfbb88c8500de14807b0300bd26a0dc9000e69e0682313031e34cad0233345bed64b97aae10dfec53aa5782839c74712c99540d40afeca464250e5e7378cfb430272bb65a0dab4378bc7310c9f68aa179c772a133449cac61e72f7ec13c2b6f4eabbdb0b061ede78fdb938b533f1f069f045c2d4fdb2bc65bd0edbcb3ae4ec60d32afb41cc92e93496e2d8d3c841beae78a4bd557066e9991f157f46d3b7140376ee23fe32672bbb272985b2e4eaef2e24a11cb6d1684acf5a823408e98c670e6c141634ea91de815c8f53c0a60ea06a568a13495cb693bf48a57441396c2c2d3d544d9ec9cd58cae74e5e1c482e9805692f48a8b1a7b109ded25ec703588f81abf14c85eb58c46af5c405cdcd427c4fc143760664379b4c985c38b23e884ba85506a35bd217ffd4f774a56a29e5876356d0da86866bb379462e264410172f2126ad518277aeba34e2c5dc6d148184b120fb3f6b4ed6cced9b3b7abb9dcfbc322765039629192eec990e10056ad5d8596c9f28c62971479f0962ed00ef1d1f20ec2d56a8d1d4a3740817be1f2a2f8b438553cb50f5e28ade5e8a14db26b01f55474ecae4d6a14feb7a24c2acabc7565e81d50b95ffa9fe8531be4a4f911ef713397e5f0e6ffcbd51f1983bb5af13e5c679d58148cb19909dda076c462008bd3b85aae1d00be57138fe6745d469af577e9955e40b408e7b66a5326f88005cfd01b65d1966fb845f814f7f176cfdcd0965e16c3ee970ac1c77a0704cd4c1b0bb2ca8baf505574440969b34fb5f043ca346c29b8f47076a7a8b5cc11308a2f90d44d4f012d6457bdda0c87244b894e0c01fda53d4b9537ac898c24b3b9dc07185072c7b4434bb92fccfb0bca122ad413aa473022c741b10ab5d03c4293471730ad56495673ae17fe625d6ed3da000f598e7e823b1d8bf42b05c632ae256e702045b74ac1c7591fa6425851092844b951ff89e52dfc93f5c34fa7aa4c32aa014faa5b7a3176d2b43212ad93404509a918160b07af25ac6502ee608b38f7e7859bf8b26680e49957717089370322343420ef2cc9ec9a73f45abb101dd7e9313f6020c0d7e6d2c9515d797bc5753bc8838736c24145362b3c17d8335300630862e2e19029a18320031ff911c63e2efc58ae5aa94c081a4e7498681fccd833c8c1f11b2763afcefcb5ddc88d66b66cc4ba8da0926138ff51b92a5ad1c477c10c62bcbc74267b49e8b91cf765b18e7bc43aa903079a11628b0824c05ea5f4b25621adf888045eada5fa002ef107913e0f3c2952c7ea54be3d38645dca5cd970e863456ba91bfa031dff7295cf1a7bd368b867c46a2126a31fc84e5f4066578b35a7da462436e121289b914a9c6e25f4ad7c720ed521f9c979a6758f350280946bbb14b9a9d0986064e6b6b41f312df142c8519af87fa7a415b1efd54d7851511d659e11e535af7f2fc1c18f131dbb4a9ac45a5fdd51f8e2fa8eb5c61c0b8345222b7504e45a1b1956ee789e622fc4a121025163134ccb9501e0d45b443156c63d721e0821a20eca2a5eca0cf759bdeed0d66e01e48a388a6d1623da962969b59f6d456a2dbf9959b04b4c206d108dfe4ce0bce671508aa5f7261afc0ea8ac35ac87180dfd51caf581c60c5c0f470498b8533d7e30aea32c175f1048d08cedac230c04fed9f8d9d066dd5899749fb529d8c61877e34174d13536c96145e300f380457326c3b30f507ca6ea8a7ae12b2d5b9cbf8aa36d522d63ead258b4be370fa6ead2edace8f55161cb609e9a44e9696166ac0895816c5a641f71f87e48521a197b0cab46ab483b4bfe2d0eca82f1a2c81b1001fd8908b4ed9ffcc26a351133253683b2c338961ad8167394ed5b156973999e8e873c713cb4fa4d17c64929c85e2d7926aa5d2427964100b5d43051eda62796a22296249b136a90112d492602bb684e5c8340b4543211ec4273420d12d132c924b08bcc89358889162493825d3427ac41205a924c0676c99ca8069128960562b8749898b0f095822752ad326292c422508a86886b158849251641293444a8552226935802a5c810b156313141622928454384b50ac4248965a0940c11d52a12134b2c00ca52501c0347e465dc0b395d8f97aa7a4456cd9038855c2af05a335465452a11b3bdfafab0653e694a2a21079e5035063669d7afd8a46d25fc2e3c7d7dbe8d2bc1b3497576d17ba84dda612c34d7d926ed4c0b1597caf53669d7feb849d38d0270f4ac1b63c8ebf35a25ae2716f2bd49fbcca019f29bb4f9504cc2483baa615b2a5c464926a0e93a98fd381451e9842538c6ba7c018529ad3869174ee3a45527f83a899d8e8f93ea042427d5850d0adf3e73e334386937952e27ad702547dc01fa8706afe29cb41bb8e7a45df93b71d23ef02fad22e17417272a807556449332b041d5d8801736c670af0a098b047574d5649920907ad2b3c833a346436ae8f8a6d3a2823d0abb694e9fcc6910ce603da00cd87211c72c5bf68a7afa9f3c7d7dd35231b5cc23b5cacc946fe34f4ba05f25e003eaabb8e3e9d29ef14e9c8c95fa1461dbe8eb739c7e8ea42fd313a329b871eb1742d9ff2f9bfc892bbfa6d5a8ec47940bc0f7438d75bec4ab1ad2baf3e224f15bfc4963cf502b7151516bb3fd4deffbe10a956c15b2b16de6ea42c3b613e466e6a80d70e3d8a053b1ba3d59d6b5eda70a97100e52154c9301304d834165496007be8f7a70e9bc07ecd8a5d6d8c477cb14dfab58c00de63b98484819e88602b986c2fad9e1ba62212bc129990396265bff35d2f492b04ba647fa7c0918c8ab26001544da2ae7a3b0fe7f508074b77001cda582f7e35a6f54adeef62e8c0aae63598e45a8b0644153adae11f4b644a8ec46869cb05aabc2764b51e0498124b5394652d915a5b58d8d3a60375581da6bf89aed73059b3faf09a9426e808d9538a0ebd6aa55986e298a7d29101237c44829bba1b636b15087ed252b507a85a8566541a95ca85113f6836cacc4019d3655adc2ea4ad12eca035138a1454cd90ab5b51583ba6ea85b82d12a43b92a054ae58a8a9a5527b12ef1b6940c374f6e0baa6de36cc7c216e7dabead9daf5aa8d416ce3826e993260bdcbf228a94b11dcac6463cd0f186ba55485a25a8b7ca80d039228e90b71bc8ca463494cb96ba45c81ae568974a8190b9428992b719d8c25e2c90d396b255303ae528974b805339428a92b62b10a78da570454d20663f8239ef355e334c58440d2723d94e7ef6021186528f369b91f86bcc3febe8bd8b9e249f7c6cf95c518ef14a6dac3d171fec752f569095148706025b1a3596041077fa33f90d290263dc9dda44fb0ab5a8a016b28e75c2e40382f8f7417408afea8b9f8596b84923b3c327957e5fc3dd3aad48056ce2b2b4297b1a358352eb30de177c1d6743d8803cdeb5809054100849f841056d13321c6ef881604b2c1d0ff9ccebb70038cf2c0e9f43556962fd7db64cd9219b0edce61db78d625bd3d707f48747ed7f7fe596192be904b399fea6028f81882bd79bf507515832181f91ff5c4ac6561c8ea33daf6730ae3302f4879834801cd9ffb385c820c24b04e2abcd43d12e40e47ae96b9fcb86a056852d5536ea3fa45423f22590a4d67524fe0f22f1ecd6e18634e5bfd97b9c3f7331a31979e0d8d96ff164951c704ce3793ab0afe97c9a16fbf6563a04d5a14b875afb9d4371a9b18cc028e50644ba2ec7ec9fa459831fa52f5718b81774d159cb072de9d4a0655baa4433464f5bdc136a6e47934326eb9ad97c0fd59a096ad5d140874c88ea7af607bdd87dbca01d90694becc4da9fde2e3b6660ad8b895599f630055927758cf537b4ba3217396a3fd94f1e1808897b43b906b239967863a2cb390576d3e56cd92a9c5f530f03ff2909f67f72a2fcd7143d455961eb15a4c5e7afd1186cbfe88ed0c90f224d0522af0a19dfa71061fb1524ed0d8992fe7fcac4fe5f9e9c4b14486894d03285f4e91429a5b2885461af551a8b95c66ac51eb9725eafe0044b82150beb358b14d1b2a66a61902dd552b734542ebe972e0757bd209480493e350c453a46f4426687942159cb941133d5d58c9f9cf941cf60099af0150d35248d1ca2665cd5f02d6bba754d2f858d8bb2b94cdab0b54d2471c345dde8296fa6eb1be602a77385d34be258d73857450eadcac9fce9915fe988963a44b48e6cb1b35bed702d772ae99dc282c752c57359f2e04cf324a67ae8ca1ec1ba6756e143553e85913e2db58f9be2e781fa41973f99f50f19024855016d2a810869a0ea22a84f05f951067de9204242285b09312185546ba1e916437c72a8503d544310b929a26f920892264a218a585591b2b2685d17d11346657494517def50353403c0bc9433696ae0a954dfb84a3e5f63ca3bfbb42f81fb8c14deb197de0e532a7905a19dae48b78eff0e3a807f333ff928520e9c9d465a90bab10f33d211d11e3d0178245fc9f4741e14858c9e58dba0b74ce5874faf08eb6d6fefa44602c65b441b5cede74cb3e86430f3edb5f0e41c55d0bdf49a3797ad1a4c191407f1e18362f7fedfc8dbb7cd50d2aa8f136ef40be0d4e9583d5ba8e8cb4879d9cdde86ffa3ae3bfdaace15b721ed4afc168be9513bff3a43409f480ba2cbf9ea13eee905d84a0d6c7cb5d5d15372c1e9ed134b1425231f799e6daabc4ccac21ab1b5685608d3c249ed63193116c435896f62de0e2ee730f7bd3f4761320f0534382c9edccb390ea1f85536dece8144d116081ca8cd7e9a8c15bb26b8dc2216c77f83576a51b306d333f335ad609147b00a3453506206f2df65b91dcde919e16f35bb1e6eb19abdb11e84fb1fcc902c7849a19f0c629121001c9b6e8c31bf094a92af868d5fc172b2580fdacce96151776d199293724b9bf0f7c6e5c9b20a03b16d58521c7373e633240c24676eb6ae61cde57cb1bc0e342c0f2e9efa0099c76fb015912a872faaead311ec2f146e149fc9b9b25744f5da0f816d43585d72b0ed589fbc6ea9418d5d72826c278a51d73a9202df751faeecdb32fe933d822a7b75c3bfdf7cae26b41cc6b6961dc49fc98b763f6502988164b2ec20baec8223c264ccc42ee1021e40e531213bcf73f9c6fa96ae78d0e39efd7e65b74b8107ed42466ae1998dc2841d51af5d6c6df21675ce4397a42e92401a22120048141473125f8c47468bca9e0042d89f4f16e7e967e4f789948cf94171ba0558b184606f46a9fd5ac33d4f4353288153cc41de7d422033a99cf13b3c7c53d41d5414d42eafb1784939dfc411d13697641deb082e85ad456562d0b65b38ad38d7b8c829769caa30360520b24957d0f2e8db339262069dede6b4b59b7cd39cd5bbb9d88a60e02cd3a1f9688682e2d3813dfe2e528e5fff196e0239dc0c9b2e5d52aa75a9480d52c414b174d1091addc5f19dcb5aeef0fd255d2e12554adf24d0aad32f59225c1d2d1f923c5bb0e4c3aa70a495836f2ccbb650055854868792432e75bdda7c65bb8e8ec99ce3bdf5fe3f32fd5e6f75f7d8f067b14633fa9d2431cfa1a661973774dc013317ceead01824e6c759b8589a08cdc6fba0aa52203a07f9baabef270e4517eb728949b63d4ff5c5dd9cf851ec553a1ebb29f161bd3324e4f5b4b3d52985fc8362340f4af2d962481c87d142528cd6a2c11c198c8ebf5806bba3df2e4b536b77d05c1925adba4a00dc6468820b278860183488105007220ae78d8f790e9111cfcd0412b3148751f3d76d562cd43dc3639120982792c117d9aad19612492dadb32b4d0f4484ae4e8a4f6aba78a6a1ae41ed2bd1eb28df0eca57d8f9a8631b4cfa4ea0fc92911d2d9a5e8aca992a26a321e38354c503a14a7e9cf01a7f016ada3fb1f069b3cbf0ec935878c8272098180242303b4b0fda253b1e84ca4ec48d495da34fc3d067e9828ea5bbc42a4c73f9da75a22c872584150829844f6ff24f4cf4f1483bfdca0e240d8ebe934fe610e9e4c01626c22fcea12204e43b49a6e933fb291d1aeee43d9723b88517730cfd1524a7a318d79ecd2d45c78163834b08527163a57a332b21310c9f55ae1a7e945d8d4ea73f0e7a056efc86ad6847676d516062e7178116ad02e6ab8020cc5091cb9389ac910e17df882363c5da6245fdf21734fff00d2f3f3a94fa4262aeb607ed46701de141c0b903434b0d88f2095d27b6b8b2b0288ae6883f0b40cade38a90f5b382c6d6dd61198a2e498eb354c6fbddcbad1548137ad88765e3ce2e5b891614f6e04c204f0aa26be503416261c688c33c70ff4c54190314f3c60ab6c185f488afcfbe9e567d192783ac1d1ddd4736f6389bc2e164b4a5713ff01359d6878c6f46168a0658bdde89ede74ea4d75d532ab7e4cfeb8529df1b5fe495a186aee10c643f8283646f222a00365fa94a0daf2cda69fecf517f29e9af5943b6d019b6bdc4df96ec123f37bdd34e29e2e1f99d340c88072d4b5348a379cd55abf674f62ef099cbc2cd5be2f46c349fcc38f5cf4bc24f8ec38843c0192d9514def90f9734b5e22b4507186e1083b21208ed42d141455cd03ee424a887a1cd3da2ac1feee94fed322bf3cb490c62ecf09f0ae361ea70d063f2d8b304160d2a37ce9415f2c4ff71f3f58f83f608ccd4f6046de5a76d929e3bf93df393ea388f9d36916e5f19a0714d910d3c968dea18ef78786ef2721550728fa307cd69dfde4efb6e20a03728c288303fc8eea84153f94d9c4f7ab836e83931bade5e2fd21f06bb10fe2003716286bffe59fbfdac74bfd8a496a39b260f84fdbaafdf71d0ffacc19e912b5de95a28f1b666a105eb9d5ec40e011fa34710899c8fc3f1fe03efda1461f671b61d165a6e7095ab60ef667b7f3a27109a41eac8bb817c0409c52b914dc917b00710fb8bdd9cc7ea8c5a99e9e1e8d1e46fccb717018454b0119c0eb72895ccd75c5adf833b6b9223efadf5d7486a5c1d19ff6ee7151f0826d16fa10a221ade0d44ff66c77fc1a325a1a309e5db733d80c4bcb7606dc6b42ba8980d0bb40882a912b13c94016fc38c0da030ce351d6752bed4c3d220fe5cd9bcdffd1bcfa668ae6d2135f15fef3ffe065e8a7b897e2c0fbc1bdc5e79debb4228be30716e73a467d1b287b4c81673284b5611808b6b38352224ef74e069f8f7846f6b147c98ff1c6ad0ef6ad754ad012ec6fd51ed735ea1e104c9a6f7cd8911801c716f946a0ba2b81a44bb425fb5dd4a3a3f7fe2f0ec8587fcce06fefeca15b78ae1c21a6225896fdc7f514ef76fc71bece7ad40d3a696243865186b2cd39969ba247028ef512e71bc5991b23c2add8e7d5493a01858c311dac9b7ce1ec47efcf63e4cce111de30738bd9e17736a1bef361df1152475210969354448d88d1155905d4ba4484b5c4d9c806630eb4ddbbfaafd09a6d21acc8f0d69a8efe6ba83bbaf7c4cba0dd736b32a93bf13e9f059eb2951e0ff4bf09f4c3f86e77bccd728ca2c060bf2e3b11da074338960e8d7d7d0024e3c71cc1583777893d7ec546b9072162698d1f2de9a698b0682a97e5b2b2bdeea63bf7e405841fb8b93c4fa388337bc08b2725369bc1799c61c98844716f77a51a7c12fa1c908ede8954f3f919972f81464fbe5af54e87cdc6dacdd2f0d2be0eab956d9c7036a79ffcc970da7036837dc04cb3c5f31ef48aa71ee5b157dee885273ce3798f7bdd53bc81f6f664f0894fab789a236d5b1d73b61416da2a66caed4267abc7ce963750b7c878d8b2547d4bb0188aa51ac62f7acbb7dc463bb7d1ea56b6a6c5b66abd652d6ca31d6d6975eb5bdf425bb7d2f216b6d1ce36b5bab5d6b416e465e8211f72180879ec8f64bccde4bf10050e8fe2b24738965d90d2e4c4d5719edadb57e2e2cb8ba1e8fff84b7c345d7a3428dbce3e80719dcb4d398f9837124cb60156c72139e4d3c43e060b5d06cd3ff69e5e30dee69fcd23793075d27b8ebabb3abe16b4bcedb80d8d233437f5abb4cb86b13b04c625fd80a56959285749e0fe66641ba69b54443f8f124a75bf5b0d43d23eb47a91a8b1e7344045866c9fbe48867a0cf71c77d7fe0d56fe36e41880d5dfbd915aac34056608d3b76de4103ffdf9b997e605d7b27abbcfe1f745d344311dbd701fbdfda05676dbf93c0fe5d44e01da858ef409329660ea0c649fc01c3893397166034ed6df61de6626d731682b78fb1cf080f0b674ce3b182e479a3ff4bf08827e0d41ea38dad4e38bc80f21db41cba133140ef3c6d62f9c04ab0d5e3eeeac74ea48afb03b993af442ebaf9d1d539ac8a3c03881e1d51424b99866b0f392811db731a2b0a361819b43b13b678020ffc445696ac141a28ad740c781a7f8e616ef06e448115c81dc23d5ebe498d07e0f0f2c12886ce237e93c2bacf892272eba3d0d3434525602e8ff4a11ae042840ce952937885967c76a363840adddacb93521cb1761475e0ea27f691c9968e1147aacad0f878dfcc9914e542c5a2a6110267b0906017d7686f3c493eb0979dbe66a588ba9b0dca809f5c4fd50cc6e5b40cf4b5240056235aa9c84fe030421696dfd82c0f702fbe2248308b489b8043974606d39334d6cfd9fca9d9c0bf9e497f9c9bb24694bb1cd345e292d5063d30ea9692452f29edec32ed9f5bc1548d044365f7d5fbd19b12eed23d8fb1b94b34b6ad3e09523a0c518a7c790c55172af2138373f6c22fc1c695bd791676e44010ab91b14cc87a2a27e68ba812ca82e7c102ca45c7b859089a8ec8ed84a37ae88d025d40012433331c533e8afdcb86e9993efe5b6d2cfe28071d99b2f1e8c352925493c835e3908707b210cefcf760dfc28b1a0a39d5ec77e5a0e6c31be62c88fc296a0460aac05729c9cf2a5d7e5a7e299e14d3d58edb872bfc039a6c4a28cb89a6750e70746eb8127e60dc9d387a421beb4849ba58a098f7d451174179b786a5defd29978705324368dbc81e7a8a7729b0be8f492490a2920e141e337603effd18ee2636ad7370a8e42742d892c8c80f0809e30b60d1d98ac222fb807f9828f8e531a2b0632f8e6675b40c162bc743a83d621506610ee0053a82c645fa68c33881136b908f1cc82c61616ca375e13bba40ddc30e6291224d24496ef775a341b669b0b674a55029be828341d0670facb68b9984356a0c5fc7653095c225abb201fe2e48804209cb12cac8ef9e6ad7c4b5b99ae9be2a7dd47015850c156f81186b1576642a3f593c19d777c37c24049daadcd95ececc3df9915a7a7e84b86a386e568afb0f81cca7eee1a222ecebf73b640ffbdcc83e3b7e236904f93bef1c40f4742b0af6340906f6f4bbe12ceff05a473a4fe3dc1e945aad64411b2c6ca48a0cbca024760e9134eb04a5038baa2b9c44dc8078e5d2d2be1362a5f2fd4f31fac997c40a537348842e366a65091d364b0a00311906078a0872b50d5ed89ab37438811d1b238a603dd085fef79522af9c1082c6688fde3e21e7c1334387b02d5de9fca2539ff7ed6e0079b1120b360cd760444707fc2a20f53f009b04f1aa5558439de519f5c20fea5345708397c3d2201bfd3ec9b2d8e43f9af679675de6c37aa1289abf9451132525c698d97d8e1bd6d3fdab6ba8c6da526b50f1d005f5f4761d8b02782a5b1667c6b470db7cfe1547fe63eaab30fcabdeefcc76b8334421c2c785811398160a1561f0c9dce0ffeca023dd1c008bf72f4030181027d8f114691e7873e65fdc074ff8d0167cc3811051e75388ffd464cee81ebbef9f8e88dd5427d8accd1cce9c9ef5ac9b193a00d92ad1124da16ee30271ac3e7118a5da220edb95d70e51053d1f480cf4ee38f5ccdbce0c1e07e99722f48407e0a76eb671bb423c9163316cbc2ebc4c6702a5c63721a446263bf904cd5fbebb1e683f026e06cb198ac3c2cd1cca5f988ac1b1e725d188fe738b830d4231ab5202d5dec724afc48bbcb1732dbbf425fbf03e968e85199085dc99e6a4727528cea924c13fe5d6bbbf17acbd519aca91f879f9e7590808f003e90bf302fa06904749e3a2f1428fd5fa94eb32bb7e6aacf05683396f769470d9beb31d7bda913a8d8f24c8893ca0407dbb0c0dd0a84bb3bd2fb3cf500b0771d8b5d7ea8192110e6c1a17e70e4ce1a2222c74b34ab71a266bfa35971476ccb4111937639a3617b89697e107f9a554b6ef7beeeb8ff72fda871c9a54e2ade207f936909f411915cc6451920b854dd47c0bcb33509a4b2160d99a6d6e04a6d7f4ea00d4e249a68d6251a33b6fda1358e3e439e51e30f91cec3003fbf025e7b741a0229fe9938c2ce7d0a87fe3bd1592b6bc082db8c9b6f205b7108bf02f38290310e031418adb8f0054048ec5c180b7c49cb8f95bf61ac8700deb80a01bb5aa428e8cd7928488e11f787ce5ac7e631c5a2a78c1593c31c1973b3c742a3234c602dbc2b3b41623d236664848a589027788ce015c06b6fef22ce0f6e7a9782b37f2d7d0c523990d1550408f3143065d92f166070f24dc6422f570e59ad0fca122d8e49db50b42cc67d5a6e56a46562d8538aa654e34062a97152c2a35d63871846e5324c06a1c94f4877a850a0b19329cf95e6fd796c6e9896c2b0866120fe0e152ff4e8db3ee4e36b5c32555d978a595c91b370acd4234d326c38bbc141ecddc5499f51027fa4aea417d620f848df04569ebbcdfa841e93db7d1ef7fb1ebc8a1edcade78757fa55012638c014ccba0b6776fac4a9f4611b63f9e6efe3ed74ab65ccc37137a8a8bdf110befdd71783a26ec8366075b9bdde6b646193f27ac893f2b127bece9dd760186458c2b846fe40e68c884676fc0144572669d195e01e7cdd580c4daa36c087939b66da6e4e592a13fa49809a228259e92f0903618c92ae0b96f8f9475fe8a29d5363de33cbf802d044016278a7161d85c80253e400e1f162d78261df1f3111cc20a0fdd31b68d50bb990b7bbb33f0e200e73b83162a0e85d57f862ea720e5c9a355a16d6162976ba7e1a1a997a2f74025fb2a9d42417d4cacfd2d06a2bea7b349debd69622d885ede98f979cec5e27d744ff7d3f34c362f547bd898dcf645dc1215034b446ea7a2585915d4b110836b3e6e0a9dbc23938e3c0a3a0b0351ee02db3c2da74a57b518fb7557c4f29851f74a84dac0be3d51e916d10d4da460301749c6d89cd120f7ed9f3a04ffa4a839bc6a78dc7692b45b9707698a94572aa7582422ec1aa88fa63fb23d6aa8d590652dd8565954ca1d3cc4119922f876fd3f5507f04a81146563a7cb6bb1df7d2e85ccb7f19017ac71626b85986327e602192bf23c88e653d6a99f10d71e865d96a4c69966ebe9e6f2154214067865286a526bcc9186661d0ec02bf4a345c7db655a942d11e3696e4ad4f9bb0a943abe5c9effba2735303b096b0bceed0054bb174aac0754314d968f5a0ec4d1b9e3e6485d7d32850deac12a108ebe54b4a395417f3c33cb093795e69c74998b5b5403c8af03b90c1ee9bff53a3efcb13587c4b3d469f9e43e98260c9a31142c3eca90182f498dfcf19681212e1874e8c7dd94f304cd5703d415cec4ee0afe38b7ecf04533550f792eb16228ea936f2b1b78b76bf7980c4407c69f8a96c9b5272fc7839653b97600bf26e33269d80a0232de87896d01815ef40541c895064e8ff221029c88224b874ab03da6e74a1c5bc0c20b0c2816734803145ee6ce277b8de5a6df8797ba3ca44bc38b4bd10b6aa982836bd062ed2279bd34338d6b287e837c91c0f4a13b87c8a7ad5dbd162beb62ed54e9a935b0ccebb1a0334d802b0b1686e07d049b34d9f86b1bcda3af24fc18d4a9e97e7ecdba0ba1b206911c740b6e9085c2cebad0f1a6d3a70d613c667b96c8a2d127a61b7d0abece9e28b5a955bca7257bd446170256db7f2d79c7d2bc4af5bbff88b1bfbe47d715a87d9b95817364e9383c294956f2addca44d55a1a692dea7c5165f64527aa55cbfa20df797ba2308c96ef278ff5c71e9ca6b8f3807b5b66c8d05d3c91ece6ba44119d7ca9055ce9bcf9c7e5677a79c1645b550e1ffde7e5f5773beb5f9a6ee1f84c0852a1b22e8468f82dda2a2bbbcc32adc3732c0d7bc9ab798e05265680cc94bcbc3e3ab44ebf0209632211b671bc573d543f5fcc47b7612b31e830674858dd46c986a3d14b65e10deb71d1f85cd1e2daba4a4b8d9975be1db4788531eac58f20d9d595589ddfc566770142412f7496283c109b6fbd2e6a1636a8dd63772f0c0d9ef4aeb59b13b2443aa823e2b993a8fbf70bf3ed9d997c11643af9399a53bf6c78fccacca6694cd77cea6cbd3f98ade9cbaf6ff6c76a29d73fcd4bea0b7b13e27e478ad31774b04f7ba5ca922608ff73f0b3f42afe9bc56e549861aff406f9958cbbe6100b1e35e91f1a0aa4e6e43baba2edaaab82d714e05211a73424efb7f9f50c1dbc1d39637a511365d67ba4e682be7f1b9413b7c5c4a609d930a95f396d066545019376ddcfb35d2bbbfce865e60658cf5798c41c28d3a27bd7aec3ef506f6cdeb26f635dfa0e64d4f36fd59713c55694ad6bd37b49cd6f11228ed3b3426fc12e47bf388c712f990c34d18b3b1833bf5e3e1ecae4fd81e082a5c9f0fd767bbf31fca3952ced4edee0cc14fd232493d3004999b3872b0afc837aa0073fa2da9eca57e523d61843b55efbc07a6fda3614c73a08ac10c32fb9c0d0bf18d41f986e6775cb7d287282417902d50f65b48021d70e561c7e2894def3774f1e6dba73ad1a7be8c47e661d2e3875c94e457e6b6401740080cf0d894a1ba515f07c60d5b4fb25ba1c59aac55e33a4937172b91ae0c8ac7d99004b3daaff098b1cc97d8f8c3759a94a2cbbfeb5ca2b9bd9fd764383a882ad87d75c1150003477fdce79fdf5d00f140b12c55de60cf9acaed23ea58ad4bb1c5af3d4cf19e3c216d1aa176f7f50a5c1477afa82c08b5ea54c8530f78cbd3d487c6882230c482fc38ecb07624aa036ba5ba0d03474646dfceb4d03b280711548a60b54a73b24a151b7018be841b478409bbd728bc77b1186ebf2641c12669383f13553133d4326a3f700e911a8a582b47f65e2ad7c93e7ed23171cf1af8f805f56b3b88c66ed1accd0a1c84343b0fcd54e1ca9205f2748d9336093683d7adf3dc4877320c09e9db604f64a3df684d22d68a9d1ef36d4d2086972d39cb8ce97f88e3fd82bce03277545d18d3c91696d76e6f5f151ba740bf4f58afc375b0cdf12cd3a4ca71d771a392c09e01d31492cf43ba648ae56e18f02124ad9d301f1d6b6692f9388ab564c0bd913914ad044b80f3cece413921581ec5be89699e0ab56d2c1b1ac1f7dbc9cd6860739fb605d4fa535b6cd2680094ae72fdf7e7a5636b9426009ddb0c3a09717a257f9ca49e481fa075d2f8469a86a1bd33df4336fbe10a256689d2cf0b91db73ea8ea0db06e4c37eff09f559846183a5224c0f1ae800d100e51b03b15f76a9aadf1e9117209784c8037e24f29a2f12f1fc605df6d861e319c3470fb4c40436aaed428f682bc962a023f4e3fe6f516aa8f13bb8a10778f19aaa1092b87acdde7bec77581656db2dbe60d51c9cd1a8b4121306af0a7991bb2cfbc3cf4bba74edfe935a73fb3216dfba5103156f530174bdc48a83f448cb1834237f141f54cb52f40a800fa6de481d72780e92dc46deea408be02e4015f9fab245d1c7005d681b1f918493a8b02ce542a1f6b664f7542578c158948a6904155ebd3692304ddaca005129fb327c2a26071bcb9096642671062175d6b2dbeed119c23b0b638d515b9b2054dc404dd3d7d95048aaafb2ba8094d3f6c1e36b9a613359ee6b6d35ec052d2fa5a4698c4d13d9f04b6c0d906cc88c690f66c79108a98989ca781a4be5c700779c893f7274acedca3c20cdacd0c80155d9adc7c5c956addae7e05711f8c37754bcf888568786f7a52dc9bf6238e903f9086f0e84390990641c55afeaf57679acd3f9436692c5126b4d7adb548d01c13accdf7f0477e8d1b5552b6be38209c9943b805c6c3d58db70b8920e47563a684f93f37c9d09b5e268a2ae538a9fba472c024b8986aa102d8a209dad70d17daaafb0e672051b276114b441107abf2c0d693eaefdd63f183ebf9e084dfaeefec891ee0fbe172be1c2ec047ac2b7cc2a4bed2f9468c2bde7c096587f957addabc314206db04c5a12e6641f6c16ffb82b5a58cba57693353cf64299f40dbde9d2d7f38857b84c273975f0937a883e9fb60f4c4f389ef008b506af0b0fc2d304e244a975c6d5fc03d62d149ac12c92f899417c2e64d12e21793e51d74e6e455c6d5ed66e0605621ec7c4fc7dd74b4dc47023c78abc4216a1759cadd9005e9e739b9827b37e942ecde5bc95f8ff57cdaa819b09e7ff21c47e0368b5954c33e11642a2d7382795b93f80e55e386509890fe8769056f5489ffcaa6fc888ea5fdc6450a2d1db1dc75818101d123adf5caee99728c87c88d409a248f94e2ec541300aff7794bf49cdddeb18a481b4eed252b9576836cdf35d001eda8e77cf61b8cc3200e261bd8ed5e189748126a0c5fa59c5ff5e6ffc22d23515a18382ed6cfb6a4de8afdae7e9d28a5e0f1863c9e1f99cdedbdc212819f00d5cab6f6fbad087b1a7ee6cad5f71beba79d3103077770931e3c946d25a75d170e2a0880167bc6fc26d013dcf5c40fd13ad7c9065cbaf38a6333aa2be3d25f24307d01d10b02c6d1815556765644a483d1c8bf5b46ea6f70ea65c4546aeb162f978fb00f343cc4a61be57b4429cb254f0ea3c21b4a62d922c016244711deaed83845c8ed25ed69b64fa555cad09237f5757c786ec960f620ce3e37a8c7b9546f8ac3d3da26f9d487e8acee48ccd268874b186e4c2d05a687adecdae1cdd20e680825ffd6ca291fb1091a8718f2a2f5ae541c02627979aa646fd0f70a6c563587623cd3d5f55cc03756911de1f27fac26dc61ba7842fbc50c3509819cdb735d85f61f69b02350b89c4a39a846e3fb3a1880bd6365fede07809bce1205e60bcaf707543b1c71e2b40c159cc63894942ec44b481ccf07195b469939c86ed5b4d927ab7e479de7575cf30134e5dfdcd7f0d405adae4d0c90cb0f060fb2932cf05bebf0cd7a28b9b2911dec346c951d03f837fba520f87b8af38d30075e432c8498e79917af3243a96935a775704ae4c3055f89716cbb8d913066127590b5c674c3f8197c2d0457aee02c6302a03c50e734b1682a7186c4f170db293e4665e03a89b17c13d86cdc2b79f3da083282ea1e008339a784ab93c231ee8fb2644023a4f002d3d77f46284ccd9a015630e172e249ab4daa0a89bf6ee5b20c572f0f252039b1cc7834e5cb852295d4818d266749a424ad520ed490027e85d408c5a110f7b0a2eef17f74ab25c284eab9d4cb2a2537061dff55e5ae0fb17867646bf42b20f9cb0405f6306ee745881f4a83d558b68ac4fc2743f551877168693666e23600b21a6aa40dae111116ade4f74509bce47886bd3057dd23889a0e2fdb5183be9e2c9cf78409e5e166b790c743d742d529f266b384647ae924f3bbb21159c7152174fe07c4771b35ae4ac65e7b305440c0ef1fbc5383dfbf29ec4bab7bdebe001c57b119314aedcca2f7a32d3e71ff22cc4cd9771fda989354d5a9512f38d4576370975d09285a32c440febd1341d40a6cda51911766044c99823654712cf87c53fc6b320d36a74a2e9c1baf8234a6b6b6f56552497e94887636beddc855fc375617cb1b9a7d1470be5e6c02ce44f91994ed72c88b7e3f3f0f046eb32f5ca960b9681ba9b29808b374aec5aba3694b0b037e6a7d750b583bfa05783d714c329b85f27e1248aaa72518770ce1f8d1bedbf0196f894a2ee51b79e9abc20f743201165e7f4f01f37c41745f41f2db4bc706054fdf75733c05abbf6fd1316399ee76cecc3c39e17d26f2412b1f2e97f7deb847db5771bf36590c4c2d298a9ef60c8a44ad986b19c1e119a0b6641ff166c68a59ded4b8c7504be8500d9b45f0cc8257cc2d28794900c3cefbfa19f1b2e718ccdbf31efee3405ffd3052dfb7e8ffc4f726c9834df51211cc2ee21b75dd60ad15d6118247e928b7b61c9895992768fa6ac5ed8e43fb7588c1178b93d1e6d83ceadc40aa6a1af4028c3fbda70725c022d7f701a20108a36f7f2bf46402e07a93aefbad0896ec7a6023c3af3fddc1c0732a5ba95076a8d2a5e19fabf62bbe58fc2e5c2e2eb029e6b8441b82fa7c417f4418ada475af13dd60d8f73f16ae51d67808421f768360a3a5f3d480ef3d35cfc2c126607a6305d002c13c3229555b44cea885e08e93d0e57491f54d9073ca705f6d18378a0fee1608e5a84e908ee0fc4e70720f6c63038c348c181760410693174068e91e77a28369ad98eb185d15170a2ee18ddaad8aea14931625e79b5a0d44b57649224e030fd4f52c3c501bef8343242894712ad343300a6205593c8b3ed02084d9599535f8d967a72e0e11723b9c799f34aa2b2f29c6d0c218189763c67f03d5660f283d92c100b4401d2170365eecd3246c223bced650274a37c3f890f6a1081d4fd09a48fd311758ac35d7b15ce1e75f1d2c50676fa34b6535e0c014a8630bb34d518d45684b1540f60d7a48c3c0cab56a2cb0a0365c87459f780cf7d2fa5b18e4368181d6688881ffd7da0838ce760807e351a405ea177bd62f16d8413db17656cdb11c19fdadf140a75f9175d9d0451990b9a0964d5db7594877f3cbd61d2a63c908b5403b78bbca2918d4860b3ceed76b28ce90dbd038e9da66f5bc3aae57c8ea3beeab0560a4d7f9f5b30cfde6d599d9b6252d5118aac668f36258c034025b107863996dfd15b2528ea5a12072b40456f6f11dac6f68020a607fe3f253c95121be2b17a302d78201317cd17fdd80dd2b5372af870de3aa86dbe6030cbd265d6253a54537a6813ab206ea596790495cd18da085a9d3ff4a082a9fdb7edec9a71006d80a690f2286f47c3612062c12ca0f3896c723c35fc1e6281d7f52ceeb5e2876a0a596b456addc9421251ad0279449d143d3b0343207ae60888ad1c83001adc08e3487c3768e60bf740c3c207de456ade621e3ac5d79886863207d88d41b9d3ea509bf4064bac0f8cfe96cb4fbd85a838f537bc2286b5bc0148411ca1516e87513e52c39d0d6a87e6506add5df8fdcacabc94f5c009ff4a2b7af4510fe1562642e0b8346c5208277404a6108744d6646320e22def0b8490229da1b1cc579579276d9defd7de89468d56fcf0368c06297e9d1dc12705c73300358e728992e9526d2f326997b4c920cb598eaac3f02a778afc4f58680803dc1bbfff9a8f023063e60de1af393146e11a53cc2d5c123004e116137fcf0101173efc03c6b05e0d5f5d82e1bcbf7006bf9a9a1950087b324d124e5804eb3b944919a89b891da7617fdf1d0036554b599dbf410d1ecf4315606a802a0b34050d32dea543ba12a5d322f638d1090047f03c5814fd95ebd6d7717eb0e0be04079660d4d5a398a0c602b85a7f8749f2ed684757d82c8ffd07d487ed38902a8dbd3c3d416633892bfb97a2ecfd11b031ef06fe5f22d46906823a05786ffd432f010a012f5006ca7b8fef95b5c21f9d54206c0af687b267dd24cfc851f1e3ab843e3f398bac24051adbd337999c760eddf06fc5f779460925ca5df1e4f9914f38d2cf64ce269a7b5f36cf5c4516f8baa07bb1eb5e91109464eee4516a4ab6cdcbcd08de55dff247a8d14feb11606fe5608fe2c6153e0bda747a32005bff3a3004cc9d39b670b0acdc6440d8bcc44d0ce02b8ef2bc0edd45aee99382434d51e658963b80cd00626c6feb566b255c960ff74733ec9e3c9b3c8c6bb31630687b916d134542ea0e19b2f0f97ac896e65d8a2504d144643c73fd0d1c9b399aa601f42cc4ebb26a00197d363a722066848c52b74e767771af28279491380bcb7874c502db0d5bdabcd58e671e33146e9a265501debe10e033bed2bcd035017bd0a12dd61f8b5037a2528ff611bb6778b9c6e52c2136595c590cf6da47cd9657bc34afaa97db343e1348ff58e387f53a042e20e66540d764badb5716b8a2d0e2a9860afc4adf83e213f5c4016da19e0c4e1692b88b76965b988acfaa5abc7a15ece64bb23529c54515f2f64a31be00176e6e7d4b0bb015742f2065b1ebd0ce2f273a320d22ce2e0571a619100c9f56b1f791f57e52c54793d42f7eb7ab5006bac792eeb5a669626b4a8dde122e57646371777d712ae1cce6b0037b202e0e261cfffbac412b044460f401b960ecc4fed8947d971c7e4e601836f595f0cbdca7e40fe944891a584c3b29096b016f641280cf1d12cd9e6d23bccedb1cfa2bb8b0b1f8579043d96e5c43821d8d0e0913921dfcfe2e31b1df65859a2dfc39e0ff5a1b17c43fa0f7c7a758dd6162eb2bdcd15b0605a573505f15ea3d86ec79f03e6ce1ec57850d464719e6aafe9663dd4b88cc75c79fe9a02d92c2bbdaeff571c22cc86c74f5bc27289bb5aca93721b6c3afb02cecaa836371601c33666b2efd8b04062d9a71dae8168e68603b223f88ae218243294b3197927cfdf6b2e721071bce1506a6dd647367eff7a10a3f046262df0dbf8c8c6968406735fcdacb4b43a520144d2700d7df3ebde10c704664d26a70a55e1029e4a85d00d3540b026c3013c0c3c0c3c0c3c0c5c751e8d1a7db2e224414a29a5a2df0cb4e7324a29a594524a089ff8dfeff73b8e5ae7fc595b9b61e10dfa0db20d62808058198609347231796ff4aa4bb4831a155c4cdc4ee1c24573b80edf62927e51fe39cfde4a4511b61e3e7e1467010d5ba85fd7a6f46b648911dd40a31693dec7d46993efdd7d1161db511019243d4000020202726802a8020d5a4cda54d197931419a99a085ba3922ff941825d018d594ccc97534725694907dd89b08d948484f485a1020fe8781f2a28a4218bc42e44ab756c354cbfb425d088c52495d44f5e5f0cb3dbc262b2c864d2ae2eaf98ec3dd697315b909d6b2fd070c5a4f9b1be58b11ff53e1b684edd021aad98dc779be799e2d4aa67564cce74196ae2d28adc7488d95982c62a26a70a39bf1762348cd40472a0a18ac9977f2da64be6fee37f200c31c2f040186284d10131c2e08018dd041aa998a444c978dafc74a98b172a26bddc2599694c895ad129268fca86cb645af4d4668ac91de377a523c372be420a41032b5779d3332a448a494a65bcbed83d72b33b8a49316f4c49f8ea7ad05010344431c942f4e5df5428267fc6cd94b4d6998a15504c4a2989ae14b35ba770178e018310015482c6272675aa60dd223f2c8893272626cb70496c5062cce44e4cfc4faf529a4f6c7b754e4c2ed98e99be7278aa4c082a29630323256494e1430c1090ee316010c2031a9b98a4b4fff552e34951cbc70404e47d204d18c8a1918989a72a747f7edcecc95a00020202124203139798dca5b473d219a3c88f2c31596676ce2dd6eec67025265965f7109b726d1d8246831e3a6ce04a8312067233a03189c9a952feaf98feb772878624266657780aa13f07696624265d79a7d7f20bb723484cf6302b1b3f27fd79d32326693769b6ee1d634691232698d4d0acd1a7648e9f464c0c192f7758ae0b1f7e464cb0d89a73ccc1ca2def8b98943e8f873fef5c8db32226e71cf3aadfeeab3a9b8889d61d83c5dacab89e44c424d1a9c4c79c36b3987588895fb23ead7e47c310134f8405bb1425934a9e2068146262beca9553fc8ffbbe126282ac9494deab2c3d4a3988095f567bf137d8b886829898d3abe51272b37e6cb140231013ef366dbe8d97d4aa31a00188c939a7c2a578baa41affc3c40e42894bf5624956657e9858fab36e26911e3be5fb30c9afa25a099d829acee1c324e1419fb0ca231675dec34437f9bd4b49cf35b534f43039f4a73af579ea4e4579989832e6dedec77bd1c1c31d26a795d968ba62c16bb337d0b0837217ad73ecb8c13e11b6bb22d0a8c36131c868335f1068d0c1408ac61c2668ad8e416bc5f5d2f618300829a1218749b2c9c2c89261e4e558848d64840c341a714864cfa90b4db33b0a19396a75773ea0018789e222eeb631ada90d85d8f581c61b2658675c1a95413dfac70d93425aca2addb6b374db3029a6da4b0b9b9a7236d93041e8cdb1e4d3beb90727202099041a6b98d82533b37bef359b7d76a0a186c967ae791a642a1841184a42230d93323e8d698785a6ca4781061a265f255d51b5ceea65d138c304215b4c4d57d031af98a170afb12fd14cad3a1b71a6d4c96c3e5662743b060c424868946172baf9ebb5a0e2dfeb4e8609ea4ec959ecf3bc9b02020202d263c020040734c690031a622883461876d000c3e41c9a1fffa53fdd3d5f9864d7994574f0af4f96f2a0e18544a30b13e49546b92ad1d6cf120434b83039560e953a27ab1b3f6d61e2cdf5a88e59f8d6240d2da0c473da5c29c534751626a99ff59f1af7ae74c2c2a4f4a2296806933f9bbfc2a4cbdbdf9dba39d7ee5698a8d1379e4a96aec2e4d1dfcdac544a630e53a1b49311d5dd6c1b770a13bc2da6e8a8d8be47a5808a62b59effe45e10088841383073048d284c4edaa163cb29d3174b284cdcfe521637d66cad4f981c2bed7c4e973e6a874e98782652de936a8abf8c44074f8230c4280389200c31c2f0814448fe03aea0d184c9235a7f99d2690d3262c2844dfa64f6c5f286679630e145c6b3518da3f9311a4a981c54ca9c419d88ccd968246172aad71473f2fce3c6d040c2e4a4316851b3179f3399000c348e30316ed43f59da4b5ad68d30c1db2ae7d54be249661a459818948e87d433f5ae9408136f5449d5a46ea1f33f8409d27f33c758d13b8f2c8449a373ae59b6752e59348230c983300d0f9a6e4bcc54a00184895d6ea16407f9a5a3e51713b46cb8126b71637772862f267bb4eb549b5e4d8bc66098d18bc9a15436b1cfaedd55f3629206a1975963b3e4c744d8cab2306317c54e77cbd8f46139ab0b6e8318ffbed964f25c204bbabce7899323272e0ef1d0f5ca75ef4fb936d3d9dcfc4dc6db9591f13cb08c19b79868ae95dd64790ed39c76c40c5b4c3abfb79cfc5ff34d4e34c48c5a4cbad6e4a1d6ee77c773ce42ec2c0f336831f1947cabe49ea3893b8b097ad942c611ca42288b2c267859a6e89a7ec54d8ec544fb8e9f9ef7fb2b2f2c26df879ae7b691165a55bf62825e4a71199d514c75574cce2a7df5a4bbb3c56ac5c4cbbe7e9bfea33c848c1593b359c6759d4c8b414782305430823040400e31631513d784886cf114e4594515933f669f713f99544c0a3284b436619b93deba08335031d1c4849af55c7afa924e3139c7cba262b83ced9937c5c49017a33da8687d9b9762d2e9109aba9f3332a498f01b63ca9f53ae908b4731419eca4a2df2fee2b5a2986c39ab534e79e3ad77a198142be43ee5ebfb188162c279cea92e65f060b27d62828e1a93bae9b00ebd27269d8753dd3542bbaed59d98a055b3f62859bf22837e5c8f199c9868232cb75f7d8e952a9b98b89d5378fd1c3326796b625250bade5cc764b4d2332313136dd3c8cf2996b54146844d7b8cec10030404f518d961181393b77469ea53a6513665c625269adeb5b80a193c6c3dc312935ddf66eecc77640c89b0cda8c424d35db313f57d2f22a36307c9e9814a7480d8d60cc20176600625268dcea1edca365f681506332631a9e53a6452930b267f494cb2b4d4fdb17229c62e12135fe4c7d4696aa262161293f3a5b66b7cd89fdbbd30e31193c487f56728fd95bde28889d9fe35c724328755bb11136654deeeccff7e93996004613062724eb2a3d5eeb75785125e0956508230c408a30364fcd100da5174d0a0c78f0e8080e88c454cb4b04fcbb94222bc821e2538ebf123a4c7081a0f1d5e83198a981caa82a6ed1c2db596444cfa146c4fc90e2262f2c58b5f624a9fcce71944460a40400e31d162d48fd5d2a482c544d8748044ce43ca781f3e1e64862126dcc7f1d9f3709b3ba910935cd7630a329b3cb3122126c6fb55939b46ff47348889b295ad6ae79db27c0431713f6d9397efdec6261093b3c7a8416a0ef7b117404c0e0f16cce2a56ca3e47f9894cf65838ee3a6d2cb7e98f041a7bdef189ae2e53e4c8a1ae44b887d69062d1f26a70b573168103aedb37b987caa796fbbde355ed5c3a430cd4aca64f230498bdc87d3b942c69d789834aeb71653aec368f10e13eeb734578a1c592fda61522965a136dd43d5f87598685ebef6e9dd63262d0361061d26095ddf50717e3b4cfc31630e93643c699b2cfbad9545844d0e9384908b8d9f17b623e1384cd88bc9939d8f97103a3dd08e101f0306213f980187497396c32a09cd196df4864996d77226bdcd8a39e386492362438ef586186969c3a4203d778cb9ea92669c0d9366c66c64e9b398cfd730514f657f90391f2ab46a98f01e52c4e765ecbd9b8609a23456d67d3f1d648a8649a3e4fa3ea96b3113ea1967989841ce5f105a4353986698fc2642e78cf18eddc1324c3eabb64d1973f8fc906162a510232e75b45bca44d8caf051d20391f124b80ccc18431647b673ecc39ca533c430c1e4c83caf753f52c61e3a5a49ca8c304cf8a066dd325ffdb80d860942c8ec269d437e6bfa0b932a6c26d774756ec2f2c2c4516feac4ebc9fc761726a76dc6e54d7a93d09c0b93825992e964d2ff55da8c2d4cf66c6951338818ab2c1c66686152fa70315a327d39a7b76146162677921b2d29119e325bb030a9468fc5edd11bc5d531ccb8c2a4a42c636dd02654a9a408db977c7fc973e0ec665861b25c8a5a5a45e768aa7915265690617378326d3acc549894791a4fdf4aca299d30c408430523086324f5c02dcc98c224dbd34153735516a5224618933054308230426c86142608d58abf39c551766f14265690f2222753e7389d085b57610614266599509ded251ff624c2867c9071a685194f989c92eefdbfae94d35cce0e339c3071574ea7a77c71542867346182f88e391f5e5452a7313dcc60c2c4f55152e39fd4e0f14340cc58c2e4912f7b4a2977513f95303999eeec746de2bab9244c34d5de763aa76c1973f447186314fc11860ac228fa837d300309785da6be5abc9ce78ca595a8b13c5f1fc4bc1b08438c3034108609461046ee13661c61b2450dcf1fba4cc6934548fe036188b16c986184c941db3f6e8618b78b75000561288f19459814a672cec164e88cf30969f38148b00233883041f59cdc6c6a39655f4809a24109f2c08c21941942987499936cff505eda99118492ec20a7839062af081b83a0113380f08b89a32fc32fb5083399b730005f4cd2a647c9142f29a9a20e921e23cdab89778b01f462726592695feb27674b5e4ceaeb7062c673fad729c2e66503d8c5241d47ebdc3b65d4e5ba98a0920e7a97bf4da971b530805c4c3e8febd87c423b5e0717134353a9fa48594b1ee54b27f5b5ff17b40d8501d8626212ea3b87d0a2da3607ed4007492d267db2ce5729b773a5fb0b03a0c5c4d0a562cefe460f7a76c100663141994eef778f7d623b2dc100643149960ca6d34e25c2d63418402c26cd064d5f4153f0d660192367773000584c2e9d3934cba9150b721136927cc5e49493b66f557413a2523118802b26670eaf9fe2df843ed2e3ae1513be62b6facd3acf3e8bb091b062d25f5fbacd07cd503a3dde9ced18c02a262615378a755e69acac060350c54455cdc13fb788bf6b253d100e50489e2e25b48bc52dd6312f9eb2e966627a36e7d47136981a161363aca791b3255abbc221e6af98a4824c9a8330e53972bb62f2969818359fa7e43667a31513935613f24ac8a4339a6830b2a391157c388f11cd1d47df9174156a5d988cd7a5a7c1c80e54056a9b6916ba26aee9a9e5f1afee238f8c8006341819011923638080d0606447a66272c5f7acd325b7e65134021ba898f461545252c46d92293e525265a79814cd74348e4a62e456820d534c50ab15e743e5e87773889561a314933ea5a084ae50212284a498e81d5407d99a49dabea368d575e3e5446b66743cf3ca6abd7d9aaa38896262c89d0c32a6fe4abd7aa19864327aeb6792b53a0f28267e8e574751f9b6967e6272586b17ddb451d47a38061b9e98a8c9b299dbee8ae9533b31c173f2e4196f7f33a87062a228b93f99cd9f146c6c62b2c836f5f0a0cd2ac8c1604313137f4e4fac7583e65c666272aa9f7de75920214eb081898997c334dd7509997de303f92526a7ffd812a3f4cb3d5b4b4c5cad9227b795a27eaac4c4730b4a3e5b6e8312934da94ca773644ad9b39368cd36674aa3b1b327892badeae6f55b5eebad45785e511fcdfc6e292fd888c4a4586e9bb26853e5a56303129336f8d57b4ee972336ae31193638efdeb9c62ba6c77c4e4cca829983e1d5a4d3d748484ec0834b081128cc0036b42d61a31a9f2e39c7dd02d6d7a57c1062326a750af6019c39756915bc1c62226865fbde88648ca7813808034c1862278d7efaaad5313b7cb41fdb4621561a3811d0f1b89381b88b071880932bb745493a7de2463884949474ba6f8762126780c9d31da290931e1469c38355ff6db978d41b819a223e75af6b59bb1b2ae5fa162cc10521b829818cac3764e5644cf2810134f7f5d3e7c65396f01313964435444c7f6d3fa0f13dc722a55c26cd5edb4e1878951f635f4ff4ee9bd7d98d49a9487117363bda70d3e4cd04e3eeea929e6a42718d8d8432b23b69eb19d9a76af9e237e2ca87899ea7e1b7a98641f26c2649ef330d9479b28ed156b117294061b7898a8eb9d4daf2ac8cd66e30e93afaf479508ffddca4418052e031b769874fea6cec6d3e468490d36ea3041b78db50695af946c9a0e136efc82acbfff168bcf61725f66edda8c954f59351684c1861c2688bcaffa933e9faee37198d41fbf1baf979f628a2a1c26b867a9b47a59df3f3a7a6cbc61629261e27a964f4c8c6744a404914007191ad05638d870c3c4bbb652ad8ce1fe82011b6d981ccbf2be9b55c9186210109215830d364c9072eff13288cd7c695d43ea6153ce6fc212820d354c900dd2c225133a63f8692c68316ca461e269f2d7d0f9f5bb6d344c4e412be8f40b52337c6c9c6152f86d18d1e6e3f5296698a02e26e55ad2df64b836ca30e1a388b40aaded394d324ccccf9f964d34853c4d0c37d818c3841f5f8f1dd558779b1826aa9d07d3ad98a196db08032a36b35da1d7baff316f29265369ad0d304c0eea4644a898f3c55049c3411a0ef00b93773fe9d3d0f726cf9246051b5e98f0419e92a696a4ad076d7461c259a95a8ad2d464bd0d2e4cf014326557120dd69b1e36051b5b98bc99a67683c8a7c6181965a8c00420203e46c8682ed8d0c2c40ea582c7cad1b3e56601084848192567061b5998a07bff17df6e446a8e09ca28010129a3e4b660030b932ac714f71dec5684123fc1c615267cc99c8e5e9add632a9960c30a939469f5b18c6ea61e5485c929997d0c9d29e869850a933a63b66dd69655f9a630c15df478a794aced62529820b53a9932a52c79a6d20836a230296adbc58b6ed053591b509838da6fd3680cedd5a5274c3aadbf95b2ad369c305135e7f7bbe95226f61f4d406f079d7de7ebe3371326668ea7ccf3df9529ac6309ad9ea7c69cbec875ea67e92dedafd9e2aebe49060f1d684309934ce8b014e522d5dabb62230993f3ed52ee13a192c14307be0d244c38d335ffe8977310a1085ba7921f4ed23e4a48106d1c61a208f531eecae2a6648ab08590e8383302eb16aead1df6a61ff26f1d6db356d56ea30893649f8a499e1e4d514f84ad53c063c447077c9490ac880d224c9011e2b3bb6975eb48842d4d078f1f3d6c0c61820aa753989dc6d7f288b0d5ad0d214cd878315cb73a9fea13cf6d046182ba700b7263752a9742464ada061026968aebbfece19f627ca4447f31c1cb2d8d7ac857c5309b2f26bf8916b923e3315767074908193f6c3032b2c3ecf4403c90da19f76252c6cb964bcec91cd3891713be428d7cce116153dbc5e43069167bce749097f9ea62d2b8a76c16cc7f45e823c4b5462e26fc9ad0a4e48465381df3514282b898a083ce7fccd1e12928f92d26a550263b3473e6ce1d42628b49a1e91737bfe5a6364f0f1d39e8f163a4472d2686fb7cbe4e9ea4c69cd342abb8af7cb9aeda1811d395e25af486b39854b953d0c162aee84125c2a611a8218b496a43555099c2950c3fd588c5c4ee6c2fe3779e9303d4037d2011e3e3510a7c94a4c08c7d9490c022d32e2d1bad1717190bcf1964fa7861f459ff8a89e77731e3b7c7ac3929c276e88ac9f9fcb372923921968bb0f518e1c163c4848c9484f8692b26574e11974f08dd5f325a42a26304ce8ac95e9534968e9ed44b57ab98182b9eaccb1171997935eb121e491593bb72c51053c92ac957478d544c74af9842e8d3cf7bf3bc094040487ca0de31601042831aa89864bba6d5772f97aa94534c103336fa44c8302a42534cea984f2ae9959b2e25b42dc5041d47d57f475d322debe08080063d1e052020202024a498947745cf96997dd01611b6901d3f789459a0c62826e9b520ea528e5e8e1511b6cbc10819213b7ef0e84a1d1cd240c7067a79fce881a29894257a19ca736bd87dbc880909193969eaa384241413c7afa2893a7929936b74f478138080f018300849354031493d069349d9eb474d89b085147e62f27a9eb8a4f477ba213b762093353c31499fd2665236bdfac9ec18300889418d4e4c3e35b2d2d37ee56e6d0320203b7620c38909d2f3857fcfeae1ee31a2a3e4778ce8c041c8f13112d263e4aec62626dacee5efa83da733e203ed20a48c1282031d2704af84a48769627292cbbdaeadfb40c7c7c865a0462626a78c97f4ecec8fa5743950031313f653c4d25d6c44a745d87aa01d21a7070a4146c70e923cad718949a54efe9b6b7d5ca7c78f111f2165e40b6a5862923613b3bb794d8f0e9bd701da4a4c741bf9e0b631f55dc81809292141253e46444842dcb499c78f1e9498986262949fd61204352631e97d3ccd2e28158d9e2426f68dcafea9b1484cd039e7e52cd2b3dfa52131d13e4c63b60a5fa5648f98f41eb7156c2ea57dab84c4119366d367ce8ea7dae98f0183900fd468c464116f39641af51c743aa9c188893a32cffc74b02cf5d869a0c6222609353df2cc647ace7d50892b625257d660e9727f5b3611b6f36420488d444c52f3b0d5983cc6da5084ed6a20626210654a888e1ed6def3719e8c331fa8c40f3131c6f7f5f20ccf51b2089b96f0480da86188c9315e0a9fcaf54c7d4834e03a060c425c50a31093b2c924d4c42719d4b7226c253a402134f851d009597b1143c23e5ec4640a6a1002cfefb12c3b8f29c2d6673f424a78a41ec4a4b0e9e275ce66a3995341dc5a752d9b55fb5a7939bd2fcffd31468d404c0eef61ba729af4db527906d4004439c6e8a89ea2e978fd1ffa1cb425994d4f49a7fd60dad31aabcb7d7eefc3319c282f753a98fcf0a111dd294f5bd638ef1e26f5e53e9d4f5f50979e1e2695ae98332995c7fd2ae78c78d7c8c3842f1d3be820548ebd321e2696cf9c59d22eb3183a237798a4a14fcc8fd5a699e59c113b4cbaf479c406751fd4a5ce6bd461828aaf1b839fee986845b8e4078f90919290a6c3c4990df931447aca67cf6172f588d2ab205f41fb7715a821878939e5f28d5d1f952a2907231c87491beb61fbee6ecc54233c467c906147811a7040a7d5bde65423fe59086abc01a1e4aff397452b7fdc40478f1f21243ab60435dc3069fbd6c5e35fd65e4e1b26e5bcc982b9f6874da7088f94a0b1013bb578759d640a5b88d151f22524676b98983c78a732d589b0f5064850491945c78041c8d550c3a48b395c9fc5a072aa1e04a4e4c74819bf0304a4461a52d5d58b9bd58abdf67450030d933afd67fe54364f3953e30c1394d4db4d22e46c142511b6463e46c4f0f12c281b483b6483df51d60c3e5009c90f1f212109a8518609ba67f2f9da62fd77da946b9061e2a735755abd93d0a9c1a0c618d668c232060b35223722a82186c92d9bd26ec87c083ac31a6198745629c7f424776d54030cba555da7ed95cc5a6ddbe630ea7173cce14711b6103e418d2f84d4f0820c6a74610435b83029fc64ac990819fd926a6ca186167ed4c8c2c44a1f4ab54b4d5f6c45d842ceb68447da1a58e0313212b2e3f80809c151e30a35a861056cd434eb4bed2a2de6f27daafc246f999444eed4a8c2a4e8b549efe5b3f76e6b506192cfc9e6efbac61426e58a85f5bd065d421d86187e89a8218589a397da95a3a73639310f35a230495909996c3e4b782adda10614267c967eef9cd46a4c17421d30448d274c1215ce56c3e59cc457c309932e9f9b8587eff890f550a30993624a41a84e21136163c224a5d22995428fb80eb741516309934fb6851cd1d1c414c2aaa8a18449225d46889339c8dec614359230319a8a90dfe13d9dc7bb14359030e92ad9c8d7cfd2333a4798609d4d748c375beaf6166a186152d5fd657511f7589d6ba1461126875abefcd3b7db681261b2b88e9b98eead969ed3506308934aa3a96895f1196a214caa6c2d2a769a4d9de545438d204cb0cfb2a4c4b4652165a8018409a6d59684ca2957ee2c038d5f4caacf20facf6f93aa90fc382134e8a123a40b0d5f4ccc0e71236553fcd3ac1713b3c8f153a7ffa9a6e7c5a4d361445dd4cbe2fe70b4a33cda51feb44063179335e5e5d77b59d35517933227ed9f7f0d1b5e0c46f3bf3121bedf2f2689cbd61d946953d9cf1713b32a8bc69fc610f1f5628287c94baf2f2f26a5dc9fe7d1b9e3de5d4cf8645f555a429f6a4f1793cbca449412131aa3978b49fee935d987da28eae162b27b8fd267da693a79b7981ca39ee5080f9f3d79b6985c49ca66f711a6f7bc5a4c507a3475b2dfaa138f1693c7b3854e1ae5c452378b49b9721e1d4c96bc5f278bc959f1d4ea7ee6f9502c26c9ccba9693c72aa1038b4932b24a8a578a0c3aaf9818daf6afd4856ae6b862521255bf90316695a615eec6d7daebbcb162d2a68dab7fb6b5cbdb2a26be8ef61d9d53ff7a53c5a4a8b5d1f4ddffdeb6544c8c1e9f4b670b21621b2a266fd28c4184670a22db292676b0f98d29bf266dcd14937275b8fc167574572bc5c4cff59a326817254b23c5a48c226485176d14937392318de216a264268a091f3aeae70c323e6b168ac97ef5bd295812793aa098e46ad5b93f85d794f389c941d3a5a79745511b4f4cbed12973de54ce6be9c404d59c3ffc5ffe879c139334bd8ccc6fbce97d139392b21c6390cf3e62d7c4045d41fd7d4c9e3dde9998205e594a788e10d2c684a9ddba63947d894942580c4a69b6244cb6c444f9987477cca65450252686a8b5bdeb205410293131ac879d8ea5e47e6e129375e6fe6352297a563824312988caa1bd1d72feb42e1c9198746183a7c62fd3a79533c50189c915db4cae32091196537dc4a41dd3335762cad2b939081c8e9820727af5594e10f8d0114206491a8e464c4a31e6ce79d1d227a5c7c18809da1a2cbe2f874f1eb388c997a2e71caf9f7f55e350c404bd41c7a4ebd3dac850098e444c8aef23d573fc226c213d4674201af8184987768e884922a47c32f93afadd1e62529a4f8def3bb159321c869818d3780a5ebb29e7301261e3b18598f425da4bee6d9a8aa38f944e88495b6234ba8cefdacf3c88895639eb89854d9e16c4a4ea18dfd4dae2c72c8bb0ad5e2026775b8deadb77dbe8978990bc0809700022c40038fe80c30f38fa80830f38f6700aa6d2856605a51511de000e3d9cb66df2556e19bb326757599f6e53b08af330397736946e3cf5551d3c4cd89c493f6f89731ff91d26d85697b65f5a3b3bc9e0018232364042f2260001311c7698d841dc8d0aadd9d4421d3c0f17018e3a4caeec6bb9dcd483cc3e1d26a6501e455eff68904d0538e63069b3c9e5942b68c81549e43049fa677ebe3ad5351a19f1911dc011874969436f8b5e0787c9da59932795455d724dbf6152e7a07d2b7f2f9fd1090e374c8efb368fa73c9627751b267e7a940d4a835adc2bc0c18689b152f8937741e6aee86c0d934653e7509f9272918f473b8caa615278480d57973c698a49c3e94ec9a446c3643d9972fa6e7a8fb7e238c3445321a372dd63860927dda486bd4ced201c659818ce628aa894c4bd2745584b80830c93cb4c86caafcee86e978500c718266c769edcd01f4fe73ec490e8e0f1011c6298b4a6b46465dd7a1d0fe9f051922dc01186c92986fda93b0fbb72e106c3047129e61b3ceb83e40b93c3aad43ae6cf8d4945d8120f0e2f4cce7d277adce5e53492901292119110b437a223840c12edc2c40f25366d0a5a2b87100717266d540bfd5e119971d691521a083ad001903b1c5b6836064b2144838aea7168a1a03f89d9f3aa98c5038e2c14a76d5fea6e219faea3b4550e5f6943888f0c706061729f29954d46882a9946028e2b4cea154b9f84dc32d794152627ed7c2532da559898b6c1327ab638a830398e9cd2bfce62173f38a63069345ef49c826549704861f2d9979f850ce2fce238a23041e89f68cc65b5d0100e284ceaa45e2bee06cf24354f981c5b1eac4d63703861a2a98fd12e8511d9418ea30913f75c6348ebdad54f3361c25acea2dcfc726d8e630993edc63db7e90d87580e7028016780230913c5c4b3928e9b942c150e244ccc8d41ebb9df7d8ca3234c4eca57d64afca8a917871126ad66bca08416131f84701461b2e8974e2e426e8a31a903f40110101d20118383083ac03184c97a9dd25a6f354f0787106ebb38378d4dd554cf2c6dbde9e96e5387ce03471026a5df1863b6e2e779cae200c2e498ce736c7f99d022ff62b2bca55852d495903efa62c2a66c9ae654c66449d38bc9e927db4e65d3f0991362bc98b0fea9dc3a635eae2b0043b8b18b092b7b296544250fb1eb62c229d9d59e5bd397b79584a497c78041c8046ee4a22fbbb8ed3075b1719dc70d5c4cb4ef2f555dc1cb64d08d5b4c4e497f8c5b22b355f0d862526caf70b329c98ccdb5b054b6532e2b09db12328d4af9b1d06282a807a1eadff2582a89b0e140c7410e789fa11de5c7c8f19cc5e4bf329d155b1b7d39594c8edea35a834547082af990327c94a8dd8845315aa4854efaf7864532c6a5acd326b485f5156ceec876b57d587c5605a9a376c3b4a8a6b862d2e76f1b7db27c2d07b56272124a88eb66f80ba361c544cd5a22d7298fac6a5731b162f2b0cdbf30a63396851baa9894ed2ace85bd0faab420201d6268a93854dc290ce48629266c8652f1e6c44edc5c8a49d1b26d0eab8d1493f37b438cdf7ec8b24731d9ce345c5c955ce89ce41ba298a41d459e4ad53cb5b00e9088f9b8118a492a5999c8d4f4bb3d45d8ea3070031413d5b3ea8653aa3f4201c88d4f4cac3c115a1fa32a6f1432521242860e1b90b827baeb4e9b911bb3ca9a2dea22639e15614b23216ce7110a90d1b12305de89c936329ece1e8bcb41e3c4842d71dbae9e8305f17b8c9084e0d08d4d4c4e2a4d484fb93ecf7567e7862626eefc79dca4de3f8b6683e751968964b436c3e2d5e332dcbad4c7fc7a716a377e72700313933ebae9bc7b34bd25bec4243badbd6e52369f6e2c3131f45c582c994c8988e88c8851894959273b4476d753da44d84674dc0d4ad88d49d8df90c4e4ee0b96abb6dc333d9140c10d484cee381a5f62b9c36303f325c46f3c6282855bba18eebee188491e2b3f6e48eff86891c1430708c8888e1b8d98ac31f7b5eef79cae20464c4c8f95577a54ce56cf22eeaacacedc76ed76d750515ad3686b3a2584aa8849bd1a6bc55654eb06256252cc612e88f5274f9963861b889894c35cca318f675eff7d8c74c00623232320c38d434cfae06ef9b18225e97709508f12a8e08621268be5e8b41d552df3cd370a31612cbdb6cee3a950ea921e3a464cc00191121d2db841884921a4c6ccaa8a16d3786310133683f618fedb9478bc6e08625285b4fd18dd39540c076272eb6f6c9efdd6967a031013b4bb4b8a9a76fa70ff6162ba942dd59c96694c2ae1861f2665b9a439a3e2c8afa03e4caa1695f770dea569e4c3e4d67ceafa792bc65cddd88321466b55ae89f82225254fc60e6ee861928cc9f64dfce7fb5c3f76201f6470dfc8c344f9fccf996aad193e11b6901ca01e6b1bb8818789252d84698b11297913a448c9939165945cdeb8c324bdc92d3c83ced7f10df13112d2c30702019b0fd4638c1b769814d3cc28e9f122a6536ed4e15a97d735ed0e4f8b774f499adcf8bfe13f3c831b749874a65e627699395c90089b8380808090cc618287d5f2689bb229a59310077070430e1375c43afff8956efcf84068dd37e2304198dcb4392bc60a9b8829791e3c74f8286983c344517a3799e5a47931e564dc70e3a8d8ce12a5b96172d5e9bff9f8f04159da303955b4f22b95fb394d64c384f7ba98b2bf7e94bc2d6d70630d13a36877f7947354cb086b70430d1354f20a22d5db63328f6407193c74ecb89186893127a39f2a6dec73d03079548c07d3b7614bc98270e30c13dd2ade95ccdbcc6cea91f13c900537cc805e5a778697de75c73a9d4aa8a464744598878f910ef0f031929ccb3031ed28b154957baad7126e906192df5b57c8d2fe98496398a02d74d41453fbaefb6248c7ded6f4f3d435deeb3fa95132c970aa4f178649957b4525bd496edaf80618267b3e19fd2ddeda4b24c2a68344471bc90e1d253ff40b134f44d496100d1a4eef85492a4b4be9d7b8e3a2eec2e4a82f6ba71d979349b930c993d78e5cb8aaeef2c61626fabca94b21c3e2b6a38549faab7dfe63775bfe6f646192e8b134a394c509b1e10616265efcfc1d7e644ea2fb2b4c54d34fbeffb92abd5ac6b76024072ac80bdcb0c2e45c31e6fe6861c9d2d2f122628080f4d0f1bf49b8518549d253e5ac6493f0cda1c2a450552e27740eaf4c99c2c4ac1861aa54cc6041e68614269c88edad4b31dc84aab870230a934a881295cbb0d81a45d8722170030a93de7407a56b5d42674f42a2a3a4c7f3e8324a4a900f32f647193e700c188464e0c6132679e718dfc2fdc46cf2861326e8e916cfa21d316e34a1b356cd9337bbf7ac4d4deaa3568a656cd0cf0d264cd027b76a2a67b58ab98409e3f9f5ff35bbabf336dc50c2ba96d2ebd75cb98b4950537a5132e58c93f18284eb36e33c543e34eb44aec3d2c95a1a994157b8718449d23c8f38a5e6a22c7ba64698709a3ffa594ae95b471f28c2a4a815d46eba30b9eb032db84184496936cfda6dd48dedf8c1e3c61026590639cf327f1a9476f5560002c221e6831b429894a13dc7d7f0ffd6aa08db19e11182031d87576f05a937823049d6d6a9f174f97f2b3a401ff0817230328231601012821b409818ebebcf82aef86cab8d5f4c3eef4db7709731e515615b565f4c929bf4fe76ab67e38f7c24482f269fdfe5cabc1da47950848d0495945142f8487cfc1083049594b101e785a741d7586767f9243676616d8e8ac9a6b68e7a76d88f9ab3089d449c4c176723179362f7375e6a18519363c1062e269edeb9d9caefa764bdc5044b3bab9227b2be225b4c4cc992bc68139e63bab560a31613e3c99c637db61c9b3f8d820d5a4cb0b354be723ae5dd7ccf6292a7f14aa32d3ec52e8bc9e1729266050fd893809904f2ea1680b7af58ca1db08002c10631059812921f3e6804a08c220132720480c407c27101121d647c8e0044210005a0000048480ac80c017cc91f101a01f8923f233e406c00600008c0e10385d8c07c094948480100108003d8c091a30d3872b06152c533113a7c16e13efb71c603f908c1818e1372dc4a7ae838418e35942012354c3a31eadb314ce5f62ac2c69b230d93fd92da3c65ea73a52411b6e58178680e34dcc871068483111e3c100f1b39cc40068f324cdef1b4ae3b192647f3386697bcbc3a1721f990102f493a4448140339c63041ac5ec45d6c5c0b43427ac70f1e58811c6228830cc303f1a891230ca8a3c48708c98e90101c39c03049fe7fbef5eee5709a081bea28c1818ef32324c471a0e3fcb8408e2f4c76cbb12ee728f56fb71726c78a77f3c9c4c64a8748c8c88e921e20e04096f4d081243d7494ecd0f1a384f8e01c5d98d8f9630919511dbeb339b83071bc846b7dca31b32e97630b935b36e93ca295f3bf1262245a98789fdd4c48bd2c236ccf918549c9932eaf24fe1c589898e97286ac129a465639aef07a4a15a3db9ac30a78a7d6e6b866d7dd9c65a70a1fc35e5ba5534b0639aa30b94f87b2b51cfb6352a930f9f673327d39a3f79a7230220608480f21c714269dda85dd985cfec3490a93b7b34fdea9981c5198a8aaee39969272406152feec9ddb3dcb821c4f58c4e978c7bced4892c309bb5c5859c7975d7ece94b4be7fb5d89ba30993d7f2eda79c62d01921811a21071326a77c41e57ad6980bc21de458c2a4d3c92e07af51fbb1b3dc831c4a98d851ffcc36e48bced88e17428e244cb6d79c2f37689ff9377990030993ccc2544ccf252e57ef831c4798789ee1ad4653949dd2da90c30893b48bc91c45ec73438e224cb2ce7932c8ccdfaaa5d721071126e9f6934b5af392d2ef1026e81ba9d93125b1972308c81b720861b2aa8b7c05b97ae8283c46ccd9be2047102667926279ddbb4b4ca63980304956bafeca24bd1f60fc62b285f498533a1f4f3ae68b49e2f4d56834fd25966a1e80d18bc9b1f4e7b2ec7ad4b401831713ddc782b668ae3d15c1d8c524cf417ebc9710711d81a10bb5946b78ee051dfe008c5c4cd0a8a923c260e0c2115715113b33931bf1b7d624a2d5bdeb1d01080218b79874399e7ad217ca846744f89490849041a23801c31693a36c527ab4c824945ec0a8c5c4550d9683dccca8f905831693729a27b12a565be95d2b80318bc999bd545d28111a343f6e80fec70d26258bc9a92966f9c8b6bd9803462c2655f077d1fc2e5f3bc3000c584cb8d1592f4e379ba5d22bbab47aa6846200862b2606f9a6544e4237dc3760b4c2b194395b75cd8b4bd74ffa2f299d6263aac060c5a42a7191aefda664bc51e0ab98e431cf5b767bd1397b554cbaf93925cbf3de28cf3700462a269ed5bdc79a7d5f52a36252c8072575e3af86fc24609c6252c51fcb2aff76db9129269c86bda46b3ac3e94c29269ab9df5dfef3187c6d0318a498a4f252859754bf2ca3514c0c3f4a57cc38f3db922826ed79a8ca8ba70ffdc108c504b99ec4b3b9dac5e5c100c5a464236a5352fac4c418cbedb2625ab89127269c8cab8f59e7826d3a31e93e7da8784237c9144e4c32d5215c3f672b5c3631e1f256ca99d9529c8b26268ddb5b4ed92d5df69998e4773ae3da5bcaa5c6c4c4b4943777d0fcd7e94b4c529b7d3732c5302b5b62e2098f513fe6f49acf4a4cfe985bf99483bd9d9498586a5b57d5941a934d6272e526ad2965929818adbca2f87e529f484c4ea7f3578f6c1eb712242647d151754a74885ae9119377fb2dfe9a902326a7cb31efd26f12c2841a31b12f78a9c710232685c5cbb92e74d4f42c628232d5274633e68c8d22265dcabcc7cb9ed197444c4c974383eb4751b1206252f8701b3ffbeeb47e88c9e5267cff63c7ecda1093f3ef68cc9fb3869a0b31b1727c56513696944c8809961ae6f7e5fe843b88c92b3f67a61573f114c40411ebd4395a92ed652026a68c9733b2dbb33d04880933d2ee4c6ea8a037f487897a57ad7bc9d7fe427e9824b2c294e7cad677a13e4cf6ab18627359e48ef83061b3e8a052aca98da13d4c10eb24727ff35b85f43029284f2a4b2c4cf9270f93ec4fe4c5f3303b193c4c8abb389ae24a64de7798bc315e73b3ada9b7ed3049870d9541c347f1b90e132d9d4edd163c7498f09a93dae92725763c739860a55d2c6f3e990f8f1c26580abf2946e5204f3b7198b8512ff6e9ecc061f2e6532193481593a9f38689393affc9b0561fade38609da1f75ac4d3e46511b2669183fd5a2ceef446c989c5984a9d9144748cf1a267ebc606a339ee9a5d530e9d4068bd1392c9fd4a461c2dc07a52cc86455aa41c3a4dd145937b3ee5e9a334cca6d32055119e3a768cc30497df84a3907eddacb946192c5e0afaf5d757322c3a454a77d27437b4b670c9365749c6bcc9c23376298bc3dc2f24bf86756c230299c854ddee1eda20d86c9495c28a1b641ea69bf303184fe56f6991115f5c244ab8b3de2dfc3c660172698fed41cae64bf472e4cda949545bea40915b730494dc7183b3e669b2b5a98d477a593dc14198357b230e9a38fa52c9349cf56b030e12ed445ef346565e50a13bdd4da2613fafdaa62854942a6e85126ab5a4f5598245a2df662f01337a2c2a4f2f6cd1d2a77633485c96696f246e5aefd1c294cd41c7ee293a51cad446162508f1923bae2661e0a93d7af94a88eff22f5274cb418a12f84ed84096fb2bd2adb7e1a7113267c578cd0db2c7d3313267996d239b23955bf4b9898f97c7edf82b2e02a61925690a72c2609136374db93c9bb682b1226e5bfac9f43c7ab108f30f9747d49cdd42aea1961720c9e74a8247b308a30717449ad4b4184d0b107830813fb75dfcf76e497eac118c2844ddaeb84deb7f4ebc110c2a49f37d9ce77139d03461026095f3f8f7bdbba1c308030f992998d142b3be9f9c5e47b1df7ce29e2b3f1c5e4d818a4e7c5b7e0965e4cd0d8a0f205f9ba5ce1c504b1f1a93a263d6f71dfc5242146b95c7f8eedb7eb62f28d8cd8d4f531d3edb998ac723af5a9fd8f2b3b2e266915a52d6b7b8bc915c6d2b52b4dc91693baf42ad4d959b0d462825efadfcdeb9d645a4c1611b7d9369fd7d4594cb0d490212d4453306531713d7a5e3ab99c3cf4584c92d12d8ae851b9ba048b4977414ba8cbf7a939af98f8e29fd55c73d218574c188d1f1ef4646cd4b46252ea14fd6a2eac98b027b4e767cb2a26e5e93953131a33a6553161d39d12ef3946935331493ec8689d5da333544cd6a01b4e5bb89cad3dc544ad5232770ead6a32c5e4b8973bdbe5ed84a598a4fe92aa0be25f5e9262d245190defda79a1514cec2c9b3d7ac797184531497bcaab2473a79d178ac93e5f26d7e2658a5983626227afda94b24c531013e01313c38e909529e489892bba83066ddb39c67462f2dfcbaae6b8abbae1c4e40c2a07e126cc42359b9868d1b3375a5c4c3ac39a98204a655388c7fc7de14c4c0c99c52dc950bdb9c29898f431efd54afe2b7cbec4a4dc19530e25b6c48411b77d298d8c92722526a54f41cd534c9531ca94986cfbf9dfdea9bf294f62528a26eed736fc6bca9298706b29691139ed2d722426ff26a1f92d9dd5a5181213f6f286cb1c4b07393d62a2679bbe123aae7dc91113ef35553c6daa31083562d2982ee99936c9cc318c9834bec98447cb222686cb274e6c5bd29a57c4e457b98c7742058d3f11134eb66a3ce6f4b247c4a4906e1d2bc61c3cda8798b0fe6bfe6a1b6292dcd84967adf33571212656b96f0a67f93ab44b88c99fd252c59fcb15a33b8889c9daaab5475bf9b98298182f76ba9dcc4c991b88c93aba62fcba2c72840b88891b73c65c1edb3f4cde0c374f953286d7d60f9374f8d4a2f43255ccb60f13747ec372f68bc1abe5c3c4ea1cb6993fc5d4b38789df9e35b6a585cf317a9860e6a9632c95ca65c9c3a4d6ff8eee29a246c7c3e4929e57b6bcb46f7e8789b17599f2a7d50e136b533c345f554a5aadc30461993cc5c5ad3eab7498345761f229e95019d5394c4ee974e7a56ada56550e9344fbc5fbd77fac54e330d1bed5f45d985f4f2a1c26bf657ad80ab9d6a7be616289e6fb6eb89799ea86893e27548acfeaa14b6dc32431dabdaabfaf5d54364cb48b41dbf2829cdc740d932ac860f952a66a987c9552d6f4207ecfd2344c8a3bd72a4a8f8a3e42c34453bddfe14974d89c334ceadf1d69f163153466989cf5eec64def8367ca302957efc84ca6bed7428649ff2947d45fce1d2c6398b47a6a3acf736b55c430b9e3c3af09a5f3c2cf304c764f5f492d891cbf130c1373505bb2524dfc68f30b13537776b4e73ffb35bd303987b2641b45c8b331bb3031a57cb39663362531b93031a72d75496899095d6e61722cf97db247c6b7975a98243aef4793f935689659981c3276ce8db6d4b8120b93d2dde96cd1a7d397ae3041e50a97d496ac30b175464e438e27fda9c2c4312d8ba5c643e5870a13538afd0bbe9fff54a63049e9d4d1f598a1b2450a13531ee1397b6d66ce4761c25a655ef89de8f55098143ddda612a6465f7fc2a4cc185de9af652ded84c931a86ead92cd96e6264cda974b15c364c2a45399a62ae9281d2f61c2088dbddc0e09a084c9f741c5c9d196112a244012262993a17259c693498800489868f9f2ee8bd28b318800479828a2e4b252e7110d11c00893b42a645cd7df52320428c264ff14f54a67d3541b0210615295eef0ed63ba9a21c01026255dd284f60c5a2f4f00214c3a21dc4b4bb8a8dc1f018230793ff437b85d76de8f004098a483028d62a8b48e0523a2401c0c8402814018100a20ab670400a313000000101e10c6a2e188485c673d0014800348463050322c22301a128c0663b138181004428160280c0883c18050280804c2dc8c3ace015d6cefcd66e8ad4181d1da5306d43d8dfc5c104f75c9ee458db528b2ef2232f41deafe957a21f3f026d350b911c8028481e8557b1addf8588dce9ba26b257e5ceb4ffa1d712d00560d7f736e5e16d3ec1b5ea5e9a3ab71618ca02b411f1a23c53c432418987091143f54a38f93602ac6637221f49d0f09bd0dae49c4f61524200a5de12f2ad787c0d14917128d44ca24a325884e9c0229329f4aae91dc1e0af051d1c0aad17246d0a9cf02b12ac3b0b14c78247ac8514428a763905ca06f69731eb47159275b88092e6fb89fd427c7d4d0978b2e6a8f9dfb4a4d51d74a441914f1ea6a01ecc21f60fd76cb4a5fbb7d6b9ddb3ae564fb6fb96983063e4d238c2db2bae010b87af812b938c800578fd300cb58cee4b97362c2331598120b434a8f3dd54c5d80b994150366162be96ea0c76b2711dcd377002a3986a8a1392804712db69b829da6d4902ee089ce018482e803280ccd1cc52108512eab23628103efc0a8a923848e592a17003648225b0a164068f05c338bfaf94f54b6fc3fde37f38ee68ac2d4f0762d1ea2527dc34674d3e2fa50043c98e83526829b2fac2a74040c82357da1d5a2d94c3a633372c19133446eb68d5b41e7a1a38690eed5378620a53b56a62cc09dfa00f0822b4d9131ec4796cd6c36789c82234060c135c0e02f4cc274da4df366db50afe71c66283d74b68eda83a2623e812b4c14df42a2d79db9794b8120700a9711a5bd6b9206c15d246f8893fb179f1b2de84713a7eb7b5acc571b2ccd7d11a6c6e183e7a52aff2608da9a0e6f08a3e764d3514a83a8e0a2a481f056e25148730cff0283f7416074a22e7e1b9a9c5398e90fc0a359868a7db295ac6f46d547cdcd54ab6d94e09593e88ce8e16366906bf3368d7d4e21f718c75194ef6a9ad12e8e3d6ab4a1e26eec12c952a49624316590b9067a1300c79c40e8301d1e026bb2cea8f3479bb4a55dbe9141a27a8652f4d8ab13713853fcc4bb40f953ab818c3980f0085586f487703bbc2efe97142c3e7c1843165b68525a8ce33369764459b97ac38b614c9212e9297c143e4e82336570687057463e7fcee5858f7768537777ced4424f85407329c1a17948eed353e3609e89cbe1ea9f84237ffb9daad987d9514fabb143dc43644845b0281afba69a51ab025ac6c09c47c4f572520fc58d3c591c83337f1d2efb733722fa2219c4becc5edfcbf43939a2f7e36479b4f03e1a60eeec2062ca44c188e2024bcfe69c9ce940e9fb209fa20bb4cce44484f42159292667f47a5c96e7e208dcff93c1b13f73eba24ce42072fa51b23a4667e8fa5ea6cfc9b1a86606ce824cc41e8627e1d8df06bc8f23a72dfd5e5271312f149e3240110219cd13358c324041a9c245bab2a7cb68699e1411f92a889c002f1439ad8112c40f29fb633043d5e1b23707c979a6c0fb5e051b945d0db66d59f8398ecd1232cab3a6d70825955a88ccf25c8c03299c17e693220d141dec737c811684ec089276b820de89ce28e9f7df46129a842903b52d73c7edb63ab56a660e36fee3ebceddcc0ed6e9eae80954f3e43d47687c6882a41080d4ca2588e6dcaabc110b01d1a0abc6c5bc1297c389fe5d053960ae153aa71f928d18c9409d949d1c8888be4819418f6543a4110bb5ac5017d4553a4947f6767daa064fc6948b2a4942a4b483d4101c19aa70c09249b7aa99f4bdb802c7ff3638ed8fdc79bbac2e571769a825a13e0661a4727e7ae5633c490db1d281f4271852a090ca18d721a01f5236d31cb9e45ddb6b37110aa05a922012da472a0942862b1757e5a41e89db9e09badc9c0e148891cac987b0d123291027a91d5da55193622379a39015e055b8f867ee70bb5b27d6113a759a1925747988dc137624753db23885ee1346f87a19927ad0ae37e7f37db95f54bd9c6fbb1b893a18bb1a8b900a0df79208855a27ec72d3efc84dbc27c1fff99fdd440afef84afbd63b98906e2fdbbb29777440c3038442b153f86283c96856a97b3ed81ce59a0a00d725d4a8167b41b3c3b3828b36d41355d87dae8e87f3b7df46affe499e68ca420c61005865c33f8dff3fe4171881792bf91ed4d0d756768bea1266b0770547bc2c47e0b3e13e9abc6ac3ec908cb31d2a198fc93c3187d1c17f1920e99d0026265033ae103e76e310319b70b4391421590d6da7a45343bc46e2be6c77aaaf37c6dc798c150da70caf2a4a5d4089392360f6579542f8d1d07960b28d63443676ff38a6c99d00c71fe8a45d7a56f93b4d5411e93cd7967e940a0753098edf3dad41be21c5a730c024f6b6d60082461b6a345d8d92a8b4594756408fcc13ac0e0f9dc2a1a8e8a95df93a7505b3f6b6d0d2d9702c087d94e6f111c1a0ff7a44a3b9b0814ee3f0bcfd05ed249293fcc8a6351a79ffb1a6dc313fdd8ec7f4b0a132ec9264a090e1a880705d17ba25b40f849adbb93ce1a066c60bb45240b1787ddf04e87022c147cb124a290896c1a8167c1d576c0e2471e624f806a761cc931432313b9deac129ae9746eac4c1646d17705426abdcb441f4f1d767ae42ebd6e0ba04281ee21ac27280182c89843c7c933171b7c94459e023abcc84c8578a0f91b10d5073b6fe6368cfc2a273a17110dcb47db3f5d3ba2f706e392caeced9277ac93a82b81383c64fa512567f213088443eda2a59e3ebe7428949ca524e7298256c0c3aeb95390df69c73a066d512d404b2122514b7957e4148b1e6d00b84a80110d560dbd3e5f60fae4eca1b65d5304898bb4a8a31102b159d9086354234d69e5be0a9447341a269be00f128fb71c8bbf2a55b0438f98106fed019a7b5a505d5d5c7eb7f4c6e3b8cb88030704bb6b088273060845bedd40602a60aa87cc4ada05adb2aea397c4b805e3cd4a3e0ab681a5b0f283e582708f18ba4006d197b351bcd7531d2e5ff0a1589f4e3bc697c81ef097109ec9c4724d1f5bfb7c7effb3d9bbfe92abdac9e663c0c65d738b4af73001837a65471876c0065ab6b967153ad6b87b765465a356582070d7735d83a20f797ebe51b0c78bd515d751ccc196a092324ef9a804f742c496bc3e7dc6b2efa69c24b7a1c160b9216742436afe3329ef4722c54864694568698fd727837d2e75c07d296071b2b410dcb1c6a1c5649d53b85294926883b273d7c833901ae1faabf41170e47f4cea2a5868514a32cd50147f450140720c86a82e071a53ce1be308d9c5a10fd6dbc2f087283d971b2840bd3a0d49a20e06a598593e1a92170b11a1e9fa26015f81b24781d4876befa529c2358bd0a753c0ce0e6b77aadd833a0df3cc958d052433fc5bbe8681a684c43bd558a535596e2124fd160e3a461cb4555e70d4085477446fa230c4a4b48cfb791461c307038160ecdd7e12404efe8ae0f4b714388c14c7b2470eff826a22c18446654a8ea536f74009c742849cce32f2b58fa4ce272ae2c789997deb4edecf9910b7e5460f1c963a3d434fe0bfd19899c26aa02295607da39b88d9fbc9bd9c9568082238058721064bc073f7a62631116a796b68a3ca9e4480b3901d4a390d01df1e24f3ba6685e56adcdf57415836d764845b8ffb72f51cb3819478b90dd1c0ccb8e514c923381abd94e4aeb11a2d60a2a55bc5646ea596e848876bef7bb5e4b612e3d49c27e263c032b69423640def1c3a97f443158a4b30612a5daf0da4a14d807136ddf09abb97b3362a37f8792f5ff8dde651b2483d8a6837729078d24d4b4825f2c1241b8e9118004880594e529586a885ad4ff3cbe40a00ceea7da3faaf147c65756ea4f86422ab1fd32e78e0c80b2a151d2dcbbd9ba0e44a14e17589a1001ffb363d28ad92d0b0049970cf803680457f6e3bbf768379062024cd372b77ebb314d9bbc634607012aec177a79847e1d273137a1a60cc2eaf77cf0afb6a6c9aeeee34758c6e1ba89aa24450c04f18485f523c297f3a71a30329f51ef981bc94195980508b4a73fdea70ef240d950413c5a3ee0f6008958ed4e8fcf5f6cada2c76a839bfd77d7025d0234a63f84da93249ffa3f433f7884d681d5fcaa0ddb4ea79a234887fd8273f6aa7f2f3a88a50d8e02f0af895eb665e040621ead8836afc4227b6887d8a58e9d2d84330434028e100695119541596db44739059c964ec2ab6b4b0cf8a724fec3cc5110f0fd4eebe4804086853c5e66935bb72624d13cd267037091aa1d9794f738e25d1bca18ae81c0c4c9635e59b06e6afc30f3db9975fb3c47f0558ec28ea00e5b2acdce2201d6401e9ced523b5e643415722313e437870e75be2dbc8e3e015a7cfa44d896f008f859f0e1bd8e08efcc13fa7e663e95624d099a111d59dca9c00dad633153eca51d64d7807339c140ef1928f2ff90997a222b52c7f9fe0ac30fb8c014347076fe475010f1530d2e691d614380c37981250830a6aea24783bd245c553aa7fd2f4c0e5d8a035f6a999adb36e18db154c1092f04c7ef0181aa45e2824b8efb39d2527f202f530d17b02608d536a16c2535a507dc4de220efaec6e43c68d277bc5ba07d4912e251edd121401921ea74427108a0bfd3578392f7594236e1f12733ea3f94e6a2c264306c14d85e1f6cbd2f801ccf8d1dfdbed58f4028ff7038571b885f24855b911226d95b7be25c061effe52c0772b7b22f9ee3e4f61462f4beb807b51ed547dc294cf82a800829c0feaf851cfc2780ff3df6b23426a0a31b69f92f40083946ed16f1e99f71cb9e2e16cc381ce807159cfb5351ff017ddc137d4713c4410c07a36aa142846e11ecf26929bdc4dd3cdd02a03a5daf6aaa47c9d5813a02126073d5f27aa82a4b19e11d5b22c040b5f236611580a7d0065bcd2f430c8cdc98d1e548108d34943a9a80888800b3bdfc99652ada3aee38fb3c63c16348a83b79afe9e9264b0ba4fef9a1e444ca5f1de061cb18426a3db46018d0a203a8736d201ec76fc48ada0e4542477a391f50011a758268d557f38a708b67a5576c7727787e17a694158fbf2aca46620cb279983b1a5118d705fefb6079214c67abeb4874a52fef90680a09968397a01e23dc713043d07ca0a6c5b6a7c66a22197ade75c144af18212e18892451b05ccf90b2a564286befdb1d02cc7131ad641e6c166554054a0513d0b25c312e1b2acb2c2becf7b9f39ce6322a4b6bb9aab9b21e536295816892c3be2213818ae1a0339193acfa51df79a9c0aa499d9886b5243076fd2b06170425d6b1040fbf088a20d4051351355b2848294e63611435480c55439bb01a19bd5ecd0d1f0be0312e54cd883b2b02b1082ec9d2cb8b264490aa60b79c9601bd0b0af628e5ba9413d8a2875c6aa8a8c9c1490c1381996659a4518742418a6d8954d05075da07238390a73039cf15167b863d8392e80fd022171318d17cdff3d78dcc22a18832e81a4f4438926bb66ef96a7dcc2c396ba961108dee98fd05091c4d196a0405c7ef538083b399a6a96d8c7b3c73b0d514c28746ca64d0b3699a8529248ad1722b249573d047a9786cb5cc183b3272e13b2062705dba6252a6a2a3b33e417deaee51564c665a8856b4f6b1b6018e8332799b5071648946f12f457fc3498d56dd9dbc31131b9eea89dfaab6c6a8be340c8f25881b340e05de8212be80447d57352766c64ec5527a9983f7921fa8f74974813575fb8ada5c5229f4ec88976a42cfdd12852ecba179ce4a2bc77e8352d99ba64f8397d197a02aa1aeeb698c09e88bb5feaa58a791ee37739ec6a8bb3215d0b90593008280a451702130ff1a991dd129fec206459669f23eb17710a94a2abb132b7e6416ad46a57636ffa99b1bee6a202eeedbdd191845a177da18346d215ba5d700b1ea14ce26c174000054491a8002fb6b51524be2a7aca6a65644b0b21feb3fc58a99564213277be7fa8e0860c7812d9e1387990a960bc092ed210cbdee28391e41f8e4b32a68d45562b8aa6f18246079f58b76579106e98fe5be281c0093d49627b96249781dc6d9b2db024f427e7c077168a71727a2d3879fa176a3c79afb6e53ce485fd9210e51fbccacb62ecf411ddfa9ba14a0c9867964978aee510d811f079e673ecc440b21580f24299c9e1729e64e2c1fe474e041fc54f95a93808a2129e59e4f42492484bd386b359ad92c063bf9622ff2d0233f0d117a9e0e3f50d06b34fb3f634bfc33bd723bbebc21750d37139eba63d31524bae1160145340bcef3f243270d01b1ab3b646853ed9cf15b9f352860f3c3fb90cd2d2dec5ff2cc1c8c1639bf4be3cf5eb98a63b53190608a1b80f2eb7903f529cb07ce1230bd136c1a2d40a393ee91075847e8c98ffbebdc6063a4edd8be2553dfe943b559f04e3e921512f5705cf2f24f211080f9ce9a642bc193af5e02a47662797b0ed1686ffe173c42d137847c38f15ac3f5c7a2e082035bfa3499d4782642ea4fc44f1a11be1307dd835478b97cfcad231fae3ec655707bd39e61cf097061fb74c424aaac6f6ba206c75b07308880370d7b9a2aebf0ccd520c1c642d06d71e99d41e1b40cb830349f45b7bba3824ba545656d92482814a678e512e7c26b4745a2eaf66552cf31127b1d4882507a6b6f73c8dc725bb7e31b90b1b5ee9964df8f28ff0969c5bd67ed87a734e3478402c5ddc1ff1aa402291db31034e7f475a53892792d054084fcd913d890c6357755b74931173f09ace6475b937be8ffadabca6956ad02e6218be9a287c0e21d11da0e27010b82cf00206a111a31d0fc621e6d2234841595ff7d1c53c13b96e131c7a9bb256388a98decc3687e39e841556c79d2719a4d0d81b3a8965f3a529538b5b39a966ac374da3262dd5b868ce00326236ea10ce5482856813527db59f51e191a9273f24afc7b12dd847ed082907805b4ed2ade98922c0f7c66a62509d00067237676cf96b0813d89f920acb3f885274e6afeace4e893dc9e0a86a130a5d7004536043ae4c33a7b76ed62f3237598fd59a8898913a7a4ce5e4074b43f066f5a7c20875f7a097de408d6f09855d3c3cc24ec1edd83defc973f8ca376682191de86b8354d570e6edfcc7694651bbb94febaf056e25c72ecfac104fb412c48ade12d79058efeae386e71c6f0cfa0802f020c67bc4fcaabba10d9eda641c0c456dbf6e0e18ba1123641d31fa7cfdbbad0b0f1e0722417e9d4877fe0654ab1538d065030146ce1f3ca3b227460546485943c144e7b63c9b0875eea9cda66226ce4b1de6063a37f45a2327eafe5476f21472a64dd647f4bfb71e724a21de027b8096992850075091722c50ec23845b2ab390198b28ee64c906af530e12108b4656fd237629459f03da108c34e6222ed74f51b280992f622588a381bb4003efe1f899119f9b7dd202ef81dac419740b7599e2c9ee732822709977f69a4adb91c7abc3bff9cc686d0d7c8fe13548e6b01028ec04c3660c28db39946f493b89d7e5f681b8213aeaea89ae3e1def114c5580ba073f205b21e0e85b41402842ac91d89af616a09ca22edbae0812f60b2516eb053e400fa4dbe63fd34c98d478a4a1f434f8b30a665708d4c12d458605ac1b2babafd3899e5a048312c5ab9f3d7f43331f4bb25f198629702af2ea9b19206eeb6bc5a93f28b8128720c6c1f55a3e594d5cb33e196b418d46de3300c157c7f24a13ea13a7dc12d181c5e83c109acffbd232dc6a449730018b47edfee3850d0773339f41d2f50c06066a7c544834e8397febf22c1f644be9fd930faa93eaab9684d6e50aaa141951ec632ec254e72245cf9fa45c31cf1284bdabcf2acfdd04d1885918b6bcce50f5e599d993655ed433d841a9e31a44dd354c31c1f7805ad44079085f5e8a4f2a3a576cacd61f1609d01bdc7ecdab2a0f084719c37a4f6a6362fa6e311a943384ecb4cf93a5aa6009be40d83dd4dbfd9a02eba28c75480e32616b4ebb70ca2b219d3ba25eb3e72beb3e09120e3c1d8188bbef0d31a074cc672aa0a53fde59351df76571e389cb359e94f9f216547f82d204bcb8feb6c3de07c0fc9ed8a27b77df0684be2010a605ad0a27a6f5fe8ef23706bf9076b370fc3864f3ee053dab13e26fa44ad3049980a9ef58b0797e6623704a8a2eb6e217b00f05324a4ecc3f832319d15e1fe5f4f43cf0db54dd0a045ed3e70dbb0fb156f8727a60a187647844b25b981d05ec55a723104ec1c23ac5a62816bbcba99220e49cd114cd57258ba1fa71fa612517cc90fc2c0f2e8d87b84fe296625998e6aeaf864eb0bb10670d5cdbbbd9684538d5fcda1d9df41a1e12c570e4a774763e0e9d511ef0a20e71828476b03db2ef6db3c5c50d2e464c50e749f84aa4b2369cdbd315f675310a1c8202f511066e90d8bcea5a16b96ffc43a6d605439f8781b37819de677c96a01fb6957c2bc11273474695dac888ff6d09118efe125e950489e2be312da0bcd999726149f3cf15284f68063ec0bfe3a0b2cb7edf18a71f4b80bf733404ace30c92384a08dd8782226f91c219a7be5376afe76e793b4de4944f2942c225a55a35721be880b84e72253d309a10b4dd380c48e599cebfabb1c185aa63562ece4325558c1884ce4897041f47092b0df29129136e4a0285f830e385ad15d9710ffbc0c4256c0454487c5680f2853856caf2d822d81ec5b1b4c22cc18015861d8aa768e81a22489b9d17a060b95cb3d18167252125431932a80e5fd392798ce55750ffcc3964501919f813c6e1cebc2043709ea8570f04344a4fa2e69bcc64410400cbc5794efeab6215ec4f0e923376c021540467da5efb41daab987ec1ca1012c8e62d5c7b954f90e32e3acc99dc5f24322f5d93be462eb72cdade889c8b4e6bfaac5d66ebd547fa43af4c9121195572fba48556a6b7d6a4f0ddd10cf77d87d935482dbba4ec6ccc0b7f97a0521c83d6bf949621a2d32211b4b71a3c0c9c62814461bedc89038a34b500b4453b6c3ba7b6077882c5f850afdb189212a10a74a2815e44803949b1577510c064c5d110ca128aeca7a884bc6a12aa0c1e1de2d5eba1a913376de8dca58ac1b284c6315e9f4ebd3c5a7217ce849e34ad4a4d1bcc7809d5531c9d5f6d09f4752488e107a041c056e119728c1f450ba2f4d52d8af911f181735e57fe0dc45e8d8e7d091b2bc05c92025b967d46479319c3d9b6772e1c484b69e0ab5019a8f86c09091b5ae6bf85b72e728c1fc756d6c364d27704159021b9da665d1f53713eb9eb67ce8f165a3c6d04ebf8c6d28ea1f7bbdd508cdff1c8380303fb0be9281ccb77ad084a216242eabcb1b064456537405a7e330b1815353d544f6353e1b3acb869dc11989fe474c609c98fa08eb1c71d3b545e8837d4035e6316dc44d06c3fd717ee872e010e41b674c621b4a85a5ee504d0a83975c3453d28d8c0f0d7e0ca65a368d6cbdbf06da3aeaf9e70a268e7a010c8ce8a782f792982f00345e00e43ff74cf5ff77716bfec3d3e0ba0071e3cfc447f0651315191beb78c64a860bea6c207e283a127315cc2f238c52a80c928a41a81496d1e7c9bc5998f723a384cdd83a401b98766ba3fdb7c50c05a04da448f4bb5ca9055aadd6fd0cc6f08a73ef5562ce8cca20be347f5641b6843b04d7cd0bbb165f2130410b7bdc6902108dd9bd9c4e19813bf0cadb0a358702f87fadcac8ee904641ca67af9bd8a39c13b48ef31a2f014f1ef4fe7071cfa74298f9397f148f6567eb86b49c6e8f69ce06e72fd2a70b143e4d47ad9e5ca91807bf876d2007ca1efd6fbb4fd280b77a63b48a8dc6c40a19b2c5eb315d88ca50eb52ef870f0e3d5caba6a6c8e7b040d3418fb18df3bd546e002fe50a778bdd3ab164f4966f0e571c40d01a9ff490fa21ea481b3e45f0ff88b8fae7568dd196bd34edb64d938458bf3fec0d38cb182a85120bc41687a63e5768dbfe0041b1cababe1fe3da48241a6415b01c4b6e0f87624a0f5f767136ea4d84bdd4ed4e0d8dd6877e2890a18fe0d52fbdb99d82f2042ac5ad35378e426d6ca261d19fa4128a909de414bddc200f18c4bcd3545df7daac5bffabf4c2262b6d296f94906ef6a209c43861366270220bcb8d4ba2d9701f7264ecaa03aa8742a99306b4bf0aa38d1cb29a0a09f6b6ffbc29bfa0c882df5f521d6808b9088a23028542667700271b256f4787ff4b96b4fbfc4daefa17c3d0cc569dcdd414c5af3e40f23da66a8b5a9556cb0811338855510754672df0e1574c07028ace29e8dbc4eaeb1a1b02dbaba7006e985e14a9c0302e17907553064759146e2e32b8102ac652ff79073bad19ca780d7e7033b1459a0b90a4a7a8920872f02f89f4c68c4678e3dd6c72c9e2d0421a94b8d3027c08b79731e13f3db1512dfc4dcf92b6d67f169f8f0407f31225146753541dfea8013cbcc0158d3525e6e992ce95b7cb98ae28ea7de8d4af6745a89a02540f74792d3ee3e79a9a8e3d9713d595f2bb9d940a88362350a7a4937c4f91b1da21e12e6bc8e1707074720a043a07adc1b9bc05eddd9fd42efac6f2bea9c3280cc1dac049044312b76cf1ed20fd694b1940fe028a7a8466aad463b802c00377467c77baacd0c8a40ac9df750a698cbbd0e5bede8428c488b614cfed40c137398cba3b93a37ab1d2ece639eabc615d34786b688c8ff55f3f8f08c862c4d995e33f47a3d2bc962749030f700d7f193c4a8c24879bdea3c4b460b35e5df813c62a481c8612ec8e43d0d665fac3bcc6775e421241d5c6e7cdef7bb768ba69540d9346c5350bc5a02092a0d9a7ffa68452e2087fd2525322cfe6870495a5004c972941b6761efebc2d635413955373a522ddfce9bbb920160996d9d0b73ea64d6e08fb8ec6da0f4bd013096aa30d294e81f109aad163dde98a5237754eb74e32955e45d3cdb55bdd6eb4091b1d1f9dee6a59a2ff58d07fbb9cc9b39ef4dc2a0c4910b1abb0db93308c71566767d8295a5b46cbb19b3b5ebad1ad077ac5a320825beb8b98714b3df93c9e4acdefd8cbdb512e42b4d45a7c73c706f0639ec21e74550055295bd444abb87388c8b6b28f6fb4cbfd46a623dab2250596cfa7478369acf7ce5661f4817eaa41f4186cea3f85394b8a67b990be62aabae005c008692b236b09378ea313bd3f39b0271f1c0d8e00d9afa2e1cc176de734e0a144e41f5f047430ab2de4549a3e8bff69afb002b420460dbcf19340f62f18a8876c26819c68ef59909d20dcfe0d1ce5a56117048841e4ac794667380688a2e7e9a3836842838d8c11cb8466c7fc9e617993f46031e9c722ccb4fa7e31aff4d8cce6c133a733010da67fc792c8ddefb00f0e8d219963a152d34b7fda95eb2c6054204a42a4836a73b1bf4fbd8d6a1e8dec28035f4abcd435266a329cd99cfa50816a0611095b7895c93ec3337ee7f60e5ed42217a3962f719ad105ab96ad046af0e8dad3625f96df4f9bb199a627c6414b9960629521c72caf808349b6688767819431a13ed830caa883e8cf1f0c22d6b51bd712136b11f1fbf0de8b61c3c58d638dc36196ad061779596bfaf1bd016dc32c7d7c713378822eee0a25b0ba37844351ce6934c2d8a2c7d5c044ed246adf07eb6b8c8e937be9cb5eea6ff5cc4afe8ed208220e2e47ce70f33d1800cf433834a84634f3cb8915cc88e14aa7f449b92e177ee1ca90847da8d850a371f6accccb09dd95c671a121e5b5190b55f4aabc8ca3f6a4b97716ba3a9980bea418bd1422a010ccf2c4f95d8afe9dd9e776d7dd7577527ef5bbefd2e7e76f99d880963e6a60d77b2a075f86e80b26552e665b47c495dc62c72d20cf41593cff74854893a1f06c93828f5a726bc0bd93d1fb0c8a81bd6c4ff5bd7a816db0100cc7a149b54e7cc18543f3559d5e4ebe6cf7bf69fe39f3f68c8ba7168f94ea6666c5b8529b7a4d82d5cd43f92d8a49cb62ffe37bf6a4cd0c175ae909c99bcfabd96bc62b8bd79fa7dd83d05bbca7138fecec2d94772fda2f087cef80a2bdc10ebcb233fe410c05872a453379924a83dec583425ab799c23c9363d014c907ee6bd12964096a3c4b2f5cf234c3916355e578f99a50c8036337c510152225763bc1435fd74281c5902fc0b5b1c6c367b4f381d012d3bf7c81fa435a23939f7cc1f8c9a8d9292b719e08f18754d44a06ccd34640aad2d8a3d19a53da410cdcc0bd7fd2810da5a167bf0755cf40f86a0eb2480bd259ba2c3721636a219103b912058f0127fa60ef19420589b28260d9f8dae09c9ae9050fe6596e390f9a2b0104908d82f9ffce0fb8f1854c068f7ccd3c4f95dd21bd23e33a4ce7eebf8b3d5315623478a4b1d548b5c526a3ea7393ac0f4558d3da3808832312943ea7761c09a57725e57ebfb1cf628f3e300ef362cb3c69f5d29c512bcd46bb0b3499750ab85863c22b983327991920fd2075593092d3af384f006a42dc722a74d488636563b392f12b9419c4730b6883d8147f91162c125b49ce966d2db0064189ef4e94c8367a0f23a7cba39ebcda86e8564cd9bddc454beb9157d2c8984fda42aca9880658c860d8ecf20dd3fa6894642b86744d0ad259f8204646df50b08d904f85527c812078c7b9a0fe5b7c41f3c2d3081eb8f6b1774e0da71dcb5cd4b660295ea750258fb15db4b9a1779b17caba3f58af683e24c34ed131f99c1160c5eae5c66994ec38d0f6ebe5af524c4a20357eb61cea0d3538a5c78707a3d065d6164903632c831b6ec18d84f2d7109daa0bdc79e64e1fc1ef046556de2d27ebbf76f16ad73456d4245990fd208948254390ca88d5a01b5a2091cc6a7758c6b41a52ef0b7e87b17f97d4a1d69844de16c0583adcd11a327e7e2963990d711de0ffbbba94bd003a7d2d599438a121926bc517c411024092ef48c216493de957bc9d05d345bcf76265e22de95a759303d0915c2bbb5ae0f9ebe87a043d195bf1d6040d2ada553703c3b229142c16821ad50871a68a83438a02ff1d04af13c24e3a0378dc41835b206453dbcd576d2215784062ff6462ac3cf31e13f61e42085feeaa855ad60329bb73d6bd70108518dc46d9bc3e12a990210004267d8cf25d13a1bca0aedc603a356f58cde0d2ce2cb155d75d582d2057ceba20c699c0241739fc4866befbe56c17eba78e81c98c7aac44266013fc59367489514d58cc2237ef7a2194ef535a2bf0ad00cc6375cc01ccac1910c8abf353f92302fa3a7239c2f41a90aa2d26b05b6f466ea685594caf864c249f2e6eef29aa4d73854d12d4570a7fb5d6a7bfaaf9628c232b3beaafa99bf46a8e130c41515f73655cecb84db7732372ea37a1d43d15ad02",
+ "patch": {
+ "balances": {
+ "balances": [
+ [
+ "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
+ 1125899906842624
+ ],
+ [
+ "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
+ 1125899906842624
+ ],
+ [
+ "5GNJqTPyNqANBkUVMN1LPPrxXnFouWXoe2wNSmmEoLctxiZY",
+ 1125899906842624
+ ],
+ [
+ "5HpG9w8EBLe5XCrbczpwq5TSXvedjrBGCwqxK1iQ7qUsSWFc",
+ 1125899906842624
+ ]
+ ]
+ },
+ "keccakDifficultyAdjustment": {
+ "initialDifficulty": [
+ 0,
+ 9,
+ 61,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "md5DifficultyAdjustment": {
+ "initialDifficulty": [
+ 0,
+ 9,
+ 61,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "sha3DifficultyAdjustment": {
+ "initialDifficulty": [
+ 0,
+ 9,
+ 61,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "utxo": {
+ "genesisUtxos": [
+ [
+ 1125899906842624,
+ "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"
+ ],
+ [
+ 1125899906842624,
+ "0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48"
+ ],
+ [
+ 1125899906842624,
+ "0xbe5ddb1579b72e84524fc29e78609e3caf42e85aa118ebfe0b0ad404b5bdd25f"
+ ],
+ [
+ 1125899906842624,
+ "0xfe65717dad0447d715f660a0a58411de509b42e6efb8375f562f58a554d5860e"
+ ]
+ ]
+ }
+ }
+ }
+ }
+}
Rust Tooling
Some useful tools for Rust development
rustfmt
To keep your code clean and easy to read, we use a tool called rustfmt
. To access all the latest features of rustfmt
we specifically use the nightly
toolchain.
To install rustfmt
for nightly
:
rustup component add rustfmt --toolchain nightly
To configure the behavior of rustfmt
, we will create a rustfmt.toml
file:
-
Create a new file in your project's root directory called
rustfmt.toml
.touch rustfmt.toml
-
Use the provided
rustfmt.toml
file to configure your formatting preferences. -
Run the code formatter using the following command:
cargo +nightly fmt
You shouldn't see any changes this time around, but as you write more code, you will be able to see cargo +nightly fmt
make everything look pretty, consistent, and easy to read.
We recommend you run
cargo +nightly fmt
after every step!
Rust Analyzer
Another popular tool in the Rust community is Rust Analyzer.
It provides many features like code completion and goto definition for code editors like VS Code.
However, to provide the full functionality that it does, Rust Analyzer needs to compile your code. For a small project like this one, this is not a problem, however working with a large project like Substrate / Polkadot-SDK, it is.
It is my personal recommendation that Rust Analyzer is not needed in this workshop, and generally you should not use it for Substrate development. However, this section might be updated in the future to include special configurations of Rust Analyzer which will work well with Polkadot SDK in the future.
For better search definition, you can follow the tutorial from Kianenigma. Simply use "one Regex to rule them all":
(macro_rules!|const|enum|struct|fn|trait|impl(<.*?>)?|type) <variable-name-here>
However, if you would like to use it anyway, now is the right time to set it up.
Issue after formatting code
You may got errors when building your project. This is because the code formatter changes the syntax of your code. To fix this, open lib.rs:334
and replace the following line:
- Md5DifficultyAdjustment: difficulty<Instance1>,
- Sha3DifficultyAdjustment: difficulty<Instance2>,
- KeccakDifficultyAdjustment: difficulty<Instance3>,
+ Md5DifficultyAdjustment: difficulty::<Instance1>,
+ Sha3DifficultyAdjustment: difficulty::<Instance2>,
+ KeccakDifficultyAdjustment: difficulty::<Instance3>,
#![allow(unused)] fn main() { //! This crate represents a concrete Substrate PoW algorithm. //! //! It is multi-pow in the sense that there are multiple supported hashing algorithms. //! A seal with any of the supported hashing algorithms will be accepted. //! //! The purpose of this design is to demonstrate hard and soft forks by adding and removing valid //! hashing algorithms. While there is no precedent for changing hashing algorithms in the real //! world yet, it is conceivable that a chain may want to upgrade to a new algorithm when the old //! one is suspected weak. In any case, the point is that we want to demonstrate hard and soft forks //! in an understandable way, the multiple hashing algorithms achieves that well. //! //! In the future, the hope is that there will be a dedicated difficulty threshold for each hashing //! algorithm. But currently the Substrate PoW crates are not that flexible. //! We could solve it by adding a pre-digest that includes information about what hashing algo is //! being used for the runtime to use later in the difficulty adjustment. #![cfg_attr(not(feature = "std"), no_std)] use core::str::FromStr; #[cfg(feature = "std")] use std::sync::Arc; use parity_scale_codec::{Decode, Encode}; #[cfg(feature = "std")] use sc_consensus_pow::{Error, PowAlgorithm}; #[cfg(feature = "std")] use sha3::{Digest, Keccak256, Sha3_256}; #[cfg(feature = "std")] use sp_api::ProvideRuntimeApi; #[cfg(feature = "std")] use sp_consensus_pow::DifficultyApi; #[cfg(feature = "std")] use sp_consensus_pow::Seal as RawSeal; use sp_consensus_pow::TotalDifficulty; use sp_core::{H256, U256}; #[cfg(feature = "std")] use sp_runtime::generic::BlockId; #[cfg(feature = "std")] use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; /// A struct that represents a difficulty threshold. /// Unlike a normal PoW algorithm this struct has a separate threshold for each hash #[derive( Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Encode, Decode, Debug, Default, scale_info::TypeInfo, )] pub struct Threshold { pub md5: U256, pub sha3: U256, pub keccak: U256, } // This trait does not seem to be fully baked in the Substrate PoW code // But we do need some kind of sinsible impl here so the node can import blocks. // so I will not use it for now. impl TotalDifficulty for Threshold { fn increment(&mut self, other: Threshold) { self.md5 += other.md5; self.sha3 += other.sha3; self.keccak += other.keccak; } } /// An enum that represents the supported hash types #[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, Debug)] pub enum SupportedHashes { Md5, Sha3, Keccak, } impl Default for SupportedHashes { fn default() -> Self { Self::Sha3 } } /// A struct that represents a concrete hash value tagged with what hashing /// algorithm was used to compute it. #[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, Debug, Default)] pub struct MultiHash { pub algo: SupportedHashes, pub value: H256, } /// Determine whether the given hash satisfies the given difficulty. /// The test is done by multiplying the two together. If the product /// overflows the bounds of U256, then the product (and thus the hash) /// was too high. pub fn simple_hash_meets_difficulty(hash: &H256, difficulty: U256) -> bool { let num_hash = U256::from_little_endian(&hash[..]); let (_, overflowed) = num_hash.overflowing_mul(difficulty); !overflowed } pub fn multi_hash_meets_difficulty(hash: &MultiHash, difficulty: Threshold) -> bool { match hash.algo { SupportedHashes::Md5 => simple_hash_meets_difficulty(&hash.value, difficulty.md5), SupportedHashes::Sha3 => simple_hash_meets_difficulty(&hash.value, difficulty.sha3), SupportedHashes::Keccak => simple_hash_meets_difficulty(&hash.value, difficulty.keccak), } } /// A Seal struct that will be encoded to a Vec<u8> as used as the /// `RawSeal` type. #[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)] pub struct Seal { pub work: MultiHash, pub difficulty: Threshold, pub nonce: U256, } /// A not-yet-computed attempt to solve the proof of work. Calling the /// compute method will compute the hash and return the seal. #[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)] pub struct Compute { pub difficulty: Threshold, pub pre_hash: H256, pub nonce: U256, } #[cfg(feature = "std")] impl Compute { pub fn compute(self, algo: SupportedHashes) -> Seal { let value = match algo { SupportedHashes::Md5 => { // The md5 is only 16 byte output, so we just concatenate it twice to // get an H256 let bytes = *md5::compute(&self.encode()[..]); let mut doubled = [0u8; 32]; doubled[0..16].copy_from_slice(&bytes[0..16]); doubled[16..32].copy_from_slice(&bytes[0..16]); H256::from(doubled) }, SupportedHashes::Sha3 => H256::from_slice(Sha3_256::digest(&self.encode()[..]).as_slice()), SupportedHashes::Keccak => H256::from_slice(Keccak256::digest(&self.encode()[..]).as_slice()), }; Seal { nonce: self.nonce, difficulty: self.difficulty, work: MultiHash { algo, value } } } } #[cfg(feature = "std")] /// A complete PoW Algorithm that uses multiple hashing algorithms. /// Needs a reference to the client so it can grab the difficulty from the runtime. pub struct MultiPow<C> { client: Arc<C>, fork_config: ForkingConfig, } #[cfg(feature = "std")] impl<C> MultiPow<C> { pub fn new(client: Arc<C>, fork_config: ForkingConfig) -> Self { Self { client, fork_config } } } //TODO could maybe derive clone_no_bound #[cfg(feature = "std")] impl<C> Clone for MultiPow<C> { fn clone(&self) -> Self { Self::new(self.client.clone(), self.fork_config) } } // Here we implement the general PowAlgorithm trait for our concrete algorithm. #[cfg(feature = "std")] impl<B: BlockT<Hash = H256>, C> PowAlgorithm<B> for MultiPow<C> where C: ProvideRuntimeApi<B>, C::Api: DifficultyApi<B, Threshold>, C: sc_client_api::HeaderBackend<B>, { type Difficulty = Threshold; fn difficulty(&self, parent: B::Hash) -> Result<Self::Difficulty, Error<B>> { let difficulty = self.client.runtime_api().difficulty(parent).map_err(|err| { sc_consensus_pow::Error::Environment(format!( "Fetching difficulty from runtime failed: {:?}", err )) })?; Ok(difficulty) } fn verify( &self, parent_id: &BlockId<B>, pre_hash: &H256, pre_digest: Option<&[u8]>, seal: &RawSeal, difficulty: Self::Difficulty, ) -> Result<bool, Error<B>> { // Try to construct a seal object by decoding the raw seal given let seal = match Seal::decode(&mut &seal[..]) { Ok(seal) => seal, Err(_) => return Ok(false), }; log::debug!("✅ Decode seal passed!"); let Some(_encoded_pre_digest) = pre_digest else { return Ok(false); }; log::debug!("✅ Checksum digest passed!"); // // TODO idk why this always return md5 only // let algo_from_predigest = match SupportedHashes::decode(&mut &encoded_pre_digest[..]) { // Ok(algo) => algo, // Err(_) => return Ok(false), // }; // log::debug!("✅ Get algorithm from digest passed!"); // // Check that the pre-digest algo matches the seal algo // // TODO it shouldn't be necessary to have both. // if seal.work.algo != algo_from_predigest { // return Ok(false); // } // log::debug!("✅ Checksum algorithm from seal passed!"); // This is where we handle forks on the verification side. // We will still need to handle it in the mining algorithm somewhere. // Currently we make the miner configure what algo they mine manually with their cli. let parent_number: u32 = match parent_id { BlockId::Hash(h) => *self .client .header(*h) .expect("Database should perform lookup successfully") .expect("parent header should be present in the db") .number(), BlockId::Number(n) => *n, } .try_into() .map_err(|_| ()) .expect("Block numbers can be converted to u32 (because they are u32)"); log::debug!("✅ Checksum parent block number passed!"); // Here we handle the forking logic according the the node operator's request. let valid_algorithm = match self.fork_config { ForkingConfig::Manual => manual_fork_validation(parent_number, seal.work.algo), ForkingConfig::Automatic(fork_heights, maxi_position) => auto_fork_validation(parent_number, seal.work.algo, fork_heights, maxi_position), }; if !valid_algorithm { return Ok(false); } log::debug!("✅ Valid algorithm!"); // See whether the hash meets the difficulty requirement. If not, fail fast. if !multi_hash_meets_difficulty(&seal.work, difficulty) { return Ok(false); } log::debug!("✅ Checksum difficulty passed!"); // Make sure the provided work actually comes from the correct pre_hash let compute = Compute { difficulty, pre_hash: *pre_hash, nonce: seal.nonce }; if compute.compute(seal.work.algo) != seal { return Ok(false); } log::debug!("✅ Re-compute passed!"); log::debug!("🛠️ All passed, append the block to the chain ..."); Ok(true) } } #[derive(Copy, Clone, Debug, Eq, PartialEq)] /// pub struct ForkHeights { /// The block height to perform the soft fork that adds sha3 and keccak support. pub add_sha3_keccak: u32, /// The block height to perform the hard fork that removes md5 support. pub remove_md5: u32, /// The block height to perform the contentious fork where some become sha3- or keccak-maxis. pub split_sha3_keccak: u32, } /// Various political positions a node could take when the network is forking into /// keccak maxis and sha3 maxis #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum MaxiPosition { /// Allow all blocks, both sha3 and keccak NoMaxi, /// Only allow sha3 blocks Sha3Maxi, /// Only allow keccak blocks KeccakMaxi, /// Only allow a single type of blocks. Which type it is is determined by what algo the node is /// mining. FollowMining, } #[derive(Copy, Clone, Eq, PartialEq)] /// The actual properly typed config after we're done working around all the BS. pub enum ForkingConfig { /// Manual, /// Automatic(ForkHeights, MaxiPosition), } impl FromStr for MaxiPosition { type Err = (); fn from_str(s: &str) -> Result<Self, Self::Err> { Ok(match &s.to_lowercase()[..] { "allow-all" | "allowall" | "no-maxi" | "nomaxi" => Self::NoMaxi, "sha3-maxi" | "sha3maxi" => Self::Sha3Maxi, "keccak-maxi" | "keccakmaxi" => Self::KeccakMaxi, _ => Self::FollowMining, }) } } /// Manual mode, the node operator manually specifies which hashing algorithms are valid through the /// mining client. If you would like to do a fork, simply allow, un-allow some algorithms to check /// it. fn manual_fork_validation(_parent_number: u32, algo: SupportedHashes) -> bool { use SupportedHashes::*; // To begin with, allow all algorithms. // After the fork height this check is skipped so all the hashes become valid. match algo { Md5 => true, Sha3 => true, Keccak => true, } } /// In automatic mode, the `ForkHeights` and `MaxiPosition` structs define the forking schedule /// and the node's behavior during the contentious fork /// (where the network splits into two chains supporting different hashing algorithms). /// The validation logic considers the parent block height, /// forking configuration parameters, and the hashing algorithm used in the PoW solution to /// determine its validity. fn auto_fork_validation( parent_number: u32, algo: SupportedHashes, fork_heights: ForkHeights, maxi_position: MaxiPosition, ) -> bool { use MaxiPosition::*; use SupportedHashes::*; log::debug!("parent_number: {:?}", parent_number); log::debug!("fork_heights: {:?}", fork_heights); if parent_number < fork_heights.add_sha3_keccak { // To begin with we only allow md5 hashes for our pow. // After the fork height this check is skipped so all the hashes become valid. match algo { Md5 => true, Sha3 => false, Keccak => false, } } else if parent_number < fork_heights.remove_md5 { // After the first fork, all three algos become valid. match algo { Md5 => true, Sha3 => true, Keccak => true, } } else if parent_number < fork_heights.split_sha3_keccak { // After the second fork, md5 is no longer valid. match algo { Md5 => false, Sha3 => true, Keccak => true, } } else { // Finally we have the contentious fork. // Our behavior here depends which maxi position we have taken. #[allow(clippy::match_like_matches_macro)] match (algo, maxi_position) { (Sha3, Sha3Maxi) => true, (Sha3, NoMaxi) => true, (Keccak, KeccakMaxi) => true, (Keccak, NoMaxi) => true, _ => false, } } } }
fn main() { substrate_build_script_utils::generate_cargo_keys(); substrate_build_script_utils::rerun_if_git_head_changed(); }
#![allow(unused)] fn main() { use std::str::FromStr; use academy_pow_runtime::{ AccountId, SS58Prefix, Signature, TOKEN_DECIMALS, TOKEN_SYMBOL, WASM_BINARY, }; use multi_pow::{ForkHeights, ForkingConfig, MaxiPosition}; use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; use sc_service::ChainType; use serde::{Deserialize, Serialize}; use sp_core::{sr25519, ByteArray, Pair, Public, H256}; use sp_runtime::traits::{IdentifyAccount, Verify}; /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. pub type ChainSpec = sc_service::GenericChainSpec<ForkingExtensions>; /// PoW and Forking related chain spec extensions to configure the client side forking behavior. /// /// The forks here are all related to adding and removing hash algorithms from the PoW. /// The chain begins supporting only md5. Later is adds sha3 and keccak. Later it removes md5. /// And finally there is a contentious fork where people become maxis. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)] #[serde(deny_unknown_fields)] pub struct ForkingExtensions { /// Manual mode is intended for when we you are running a live workshop. /// No forking happens automatically. Rather, you have to hard-code the forks. /// /// If manual mode is enabled, the rest of the parameters are ignored. /// This should really be an enum, but I have to work around the broken extension system. /// /// Aww damn it! I can't even use bool in this broken system? Okay then I guess 0 means /// automatic mode and anything else means manual mode. pub manual_mode: u32, /// The block height to perform the soft fork that adds sha3 and keccak support. pub add_sha3_keccak: u32, /// The block height to perform the hard fork that removes md5 support. pub remove_md5: u32, /// The block height to perform the contentious fork where some become sha3- or keccak-maxis. pub split_sha3_keccak: u32, // Damn extension thing is so fragile, I can't even use an enum here. // Let alone that time I tried to use the forked value feature. /// The political position that this node will take at the contentious fork. pub maxi_position: String, } impl From<&ForkingExtensions> for ForkingConfig { fn from(e: &ForkingExtensions) -> Self { if e.manual_mode > 0 { return Self::Manual; } let fork_heights = ForkHeights { add_sha3_keccak: e.add_sha3_keccak, remove_md5: e.remove_md5, split_sha3_keccak: e.split_sha3_keccak, }; let maxi_position = MaxiPosition::from_str(&e.maxi_position).expect("Should have a valid maxi position..."); Self::Automatic(fork_heights, maxi_position) } } impl ForkingExtensions { /// Try to get the extension from the given `ChainSpec`. pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> { sc_chain_spec::get_extension(chain_spec.extensions()) } } /// Generate a crypto pair from seed. pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public { TPublic::Pair::from_string(&format!("//{}", seed), None) .expect("static values are valid; qed") .public() } type AccountPublic = <Signature as Verify>::Signer; /// Generate an account ID from seed. pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId where AccountPublic: From<<TPublic::Pair as Pair>::Public>, { AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account() } pub fn development_config() -> Result<ChainSpec, String> { Ok(ChainSpec::builder( WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?, ForkingExtensions { manual_mode: 1, // change this to `0` if you want to try `auto_fork_validation` add_sha3_keccak: 10, remove_md5: 20, split_sha3_keccak: 30, maxi_position: String::from("follow-mining"), }, ) .with_name("Development") .with_id("dev") .with_chain_type(ChainType::Development) .with_genesis_config_patch(genesis( // Pre-funded accounts vec![ get_account_id_from_seed::<sr25519::Public>("Alice"), get_account_id_from_seed::<sr25519::Public>("Bob"), get_account_id_from_seed::<sr25519::Public>("Alice//stash"), get_account_id_from_seed::<sr25519::Public>("Bob//stash"), ], // Initial Difficulty 4_000_000, )) .with_properties(system_properties()) .build()) } pub fn testnet_config() -> Result<ChainSpec, String> { Ok(ChainSpec::builder( WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?, ForkingExtensions { manual_mode: 1, add_sha3_keccak: 0, remove_md5: 0, split_sha3_keccak: 0, maxi_position: String::new(), }, ) .with_name("Testnet") .with_id("testnet") .with_chain_type(ChainType::Local) .with_genesis_config_patch(genesis( vec![ get_account_id_from_seed::<sr25519::Public>("Alice"), get_account_id_from_seed::<sr25519::Public>("Bob"), get_account_id_from_seed::<sr25519::Public>("Alice//stash"), get_account_id_from_seed::<sr25519::Public>("Bob//stash"), ], 4_000_000, )) .with_properties(system_properties()) .build()) } fn genesis(endowed_accounts: Vec<AccountId>, initial_difficulty: u32) -> serde_json::Value { serde_json::json!({ "balances": { // Configure endowed accounts with initial balance of 1 << 50. "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 50)).collect::<Vec<_>>(), }, "keccakDifficultyAdjustment": { "initialDifficulty": u32_to_u8_32(initial_difficulty), }, "md5DifficultyAdjustment": { "initialDifficulty": u32_to_u8_32(initial_difficulty), }, "sha3DifficultyAdjustment": { "initialDifficulty": u32_to_u8_32(initial_difficulty), }, }) } /// Convert u32 (default value) to [u8;32] (U256) /// in little-endian format pub fn u32_to_u8_32(num: u32) -> [u8; 32] { let mut result = [0u8; 32]; let bytes = num.to_le_bytes(); result[..4].copy_from_slice(&bytes); result } fn system_properties() -> sc_chain_spec::Properties { let mut properties = sc_chain_spec::Properties::new(); properties.insert("ss58Format".into(), SS58Prefix::get().into()); properties.insert("tokenSymbol".into(), TOKEN_SYMBOL.into()); properties.insert("tokenDecimals".into(), TOKEN_DECIMALS.into()); properties } }
#![allow(unused)] fn main() { use academy_pow_runtime::AccountId; use multi_pow::SupportedHashes; use sc_cli::{ clap::{ArgGroup, Parser}, RunCmd, }; use sc_service::ChainType; use sp_core::{crypto::Ss58Codec, sr25519}; #[derive(Debug, Parser)] #[clap(subcommand_negates_reqs(true), version(env!("SUBSTRATE_CLI_IMPL_VERSION")))] pub struct Cli { #[clap(subcommand)] pub subcommand: Option<Subcommand>, #[command(flatten)] pub pow: AcademyPowCli, #[clap(flatten)] pub run: RunCmd, } #[derive(Debug, Parser, Clone)] #[clap(group(ArgGroup::new("backup")))] pub struct AcademyPowCli { /// Miner's AccountId (base58 encoding of an SR25519 public key) for the block rewards #[clap(long, conflicts_with = "mining_public_key", value_parser = parse_account_id)] pub mining_account_id: Option<AccountId>, /// Miner's hex encoding of the SR25519 public key) for the block rewards #[clap( long, conflicts_with = "mining_account_id", value_parser = parse_sr25519_public_key )] pub mining_public_key: Option<sr25519::Public>, /// The mining algorithm to use #[clap(long, value_parser = parse_algo, default_value = "md5")] pub mining_algo: multi_pow::SupportedHashes, /// whether to use instant seal #[clap(long, default_value = "false")] pub instant_seal: bool, } impl AcademyPowCli { pub fn public_key_bytes(&self, keyring: Option<sp_keyring::Sr25519Keyring>) -> [u8; 32] { match &self.mining_account_id { Some(account_id) => *account_id.as_ref(), None => match self.mining_public_key { Some(public_key) => public_key.0, None => keyring.map(|k| k.to_raw_public()).unwrap_or([0u8; 32]), }, } } } #[derive(Debug, Parser)] pub struct BuildSpecCmd { #[clap(flatten)] pub base: sc_cli::BuildSpecCmd, /// Chain name. #[arg(long, default_value = "Academy PoW")] pub chain_name: String, /// Chain ID is a short identifier of the chain #[arg(long, value_name = "ID", default_value = "academy_pow")] pub chain_id: String, /// AccountIds of the optional rich accounts #[arg(long, value_delimiter = ',', value_parser = parse_account_id, num_args=1..)] pub endowed_accounts: Option<Vec<AccountId>>, /// The type of the chain. Possible values: "dev", "local", "live" (default) #[arg(long, value_name = "TYPE", value_parser = parse_chaintype, default_value = "live")] pub chain_type: ChainType, #[arg(long, default_value = "4000000")] pub initial_difficulty: u32, } fn parse_algo(s: &str) -> Result<SupportedHashes, String> { Ok(match s { "md" | "Md" | "md5" | "Md5" => SupportedHashes::Md5, "sha" | "sha3" | "Sha" | "Sha3" => SupportedHashes::Sha3, "keccak" | "Keccak" => SupportedHashes::Keccak, s => panic!("Wrong mining algo: {}. Possible values: md5, sha3, keccak", s), }) } fn parse_chaintype(s: &str) -> Result<ChainType, String> { Ok(match s { "dev" => ChainType::Development, "local" => ChainType::Local, "live" => ChainType::Live, s => panic!("Wrong chain type {} Possible values: dev local live", s), }) } /// Parse AccountId from a string argument passed on the command line. fn parse_account_id(s: &str) -> Result<AccountId, String> { Ok(AccountId::from_string(s) .expect("Passed string is not a bas58 encoding of a sr25519 public key")) } /// Parse sr25519 pubkey from a string argument passed on the command line. fn parse_sr25519_public_key(s: &str) -> Result<sr25519::Public, String> { Ok(sr25519::Public::from_string(s) .expect("Passed string is not a hex encoding of a sr25519 public key")) } #[derive(Debug, clap::Subcommand)] pub enum Subcommand { /// Key management cli utilities #[command(subcommand)] Key(sc_cli::KeySubcommand), /// Build a chain specification. BuildSpec(BuildSpecCmd), /// Validate blocks. CheckBlock(sc_cli::CheckBlockCmd), /// Export blocks. ExportBlocks(sc_cli::ExportBlocksCmd), /// Export the state of a given block into a chain spec. ExportState(sc_cli::ExportStateCmd), /// Import blocks. ImportBlocks(sc_cli::ImportBlocksCmd), /// Remove the whole chain. PurgeChain(sc_cli::PurgeChainCmd), /// Revert the chain to a previous state. Revert(sc_cli::RevertCmd), /// Db meta columns information. ChainInfo(sc_cli::ChainInfoCmd), } }
#![allow(unused)] fn main() { // Copyright 2017-2020 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Substrate is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Substrate. If not, see <http://www.gnu.org/licenses/>. use academy_pow_runtime::Block; use multi_pow::{ForkingConfig, MaxiPosition}; use sc_cli::SubstrateCli; use sc_service::PartialComponents; use sp_core::sr25519; use crate::{ chain_spec::{self, ForkingExtensions}, cli::{Cli, Subcommand}, service, }; impl SubstrateCli for Cli { fn impl_name() -> String { "Academy PoW Chain".into() } fn impl_version() -> String { env!("SUBSTRATE_CLI_IMPL_VERSION").into() } fn executable_name() -> String { env!("CARGO_PKG_NAME").into() } fn author() -> String { env!("CARGO_PKG_AUTHORS").into() } fn description() -> String { env!("CARGO_PKG_DESCRIPTION").into() } fn support_url() -> String { "https://github.com/danielbui12/substrate-bitcoin-like-blockchain/issues/new".into() } fn copyright_start_year() -> i32 { 2025 } fn load_spec(&self, id: &str) -> Result<Box<dyn sc_service::ChainSpec>, String> { Ok(match id { "" => Box::new(chain_spec::ChainSpec::from_json_bytes( &include_bytes!("../../spec.json")[..], )?), "dev" => Box::new(chain_spec::development_config()?), "local" => Box::new(chain_spec::testnet_config()?), path => Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?), }) } } /// Parse and run command line arguments pub fn run() -> sc_cli::Result<()> { let cli = Cli::from_args(); match &cli.subcommand { Some(Subcommand::Key(cmd)) => cmd.run(&cli), Some(Subcommand::BuildSpec(cmd)) => { let runner = cli.create_runner(&cmd.base)?; runner.sync_run(|config| cmd.base.run(config.chain_spec, config.network)) }, Some(Subcommand::CheckBlock(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { let PartialComponents { client, task_manager, import_queue, .. } = service::new_partial(&config, ForkingConfig::Manual)?; Ok((cmd.run(client, import_queue), task_manager)) }) }, Some(Subcommand::ExportBlocks(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { let PartialComponents { client, task_manager, .. } = service::new_partial(&config, ForkingConfig::Manual)?; Ok((cmd.run(client, config.database), task_manager)) }) }, Some(Subcommand::ExportState(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { let PartialComponents { client, task_manager, .. } = service::new_partial(&config, ForkingConfig::Manual)?; Ok((cmd.run(client, config.chain_spec), task_manager)) }) }, Some(Subcommand::ImportBlocks(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { let PartialComponents { client, task_manager, import_queue, .. } = service::new_partial(&config, ForkingConfig::Manual)?; Ok((cmd.run(client, import_queue), task_manager)) }) }, Some(Subcommand::PurgeChain(cmd)) => { let runner = cli.create_runner(cmd)?; runner.sync_run(|config| cmd.run(config.database)) }, Some(Subcommand::Revert(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { let PartialComponents { client, task_manager, backend, .. } = service::new_partial(&config, ForkingConfig::Manual)?; Ok((cmd.run(client, backend, None), task_manager)) }) }, Some(Subcommand::ChainInfo(cmd)) => { let runner = cli.create_runner(cmd)?; runner.sync_run(|config| cmd.run::<Block>(&config)) }, None => { // Get the mining account from the cli let bytes: [u8; 32] = cli.pow.public_key_bytes(cli.run.get_keyring()); let sr25519_public_key = sr25519::Public::from_raw(bytes); let runner = cli.create_runner(&cli.run)?; runner.run_node_until_exit(|config| async move { // Get the forking information from the chain spec extension. // Convert it to a strong type, and fill in the proper maxi position if they are // following mining. let forking_extension = ForkingExtensions::try_get(&*config.chain_spec) .expect("Should be able to get the fork config from the extension"); let forking_config = match ForkingConfig::from(forking_extension) { ForkingConfig::Automatic(fork_heights, MaxiPosition::FollowMining) => { let maxi_position = match cli.pow.mining_algo { multi_pow::SupportedHashes::Md5 => MaxiPosition::NoMaxi, multi_pow::SupportedHashes::Sha3 => MaxiPosition::Sha3Maxi, multi_pow::SupportedHashes::Keccak => MaxiPosition::KeccakMaxi, }; ForkingConfig::Automatic(fork_heights, maxi_position) }, old_config => old_config, }; service::new_full::<sc_network::Litep2pNetworkBackend>( config, forking_config, //TODO Combine the following three fields into a MiningConfig analogous to the // ForkingConfig sr25519_public_key, cli.pow.instant_seal, cli.pow.mining_algo, ) .map_err(sc_cli::Error::Service) }) }, } } }
//! Substrate Node Template CLI library. #![warn(missing_docs)] mod chain_spec; #[macro_use] mod service; mod cli; mod command; mod rpc; fn main() -> sc_cli::Result<()> { command::run() }
#![allow(unused)] fn main() { //! A collection of node-specific RPC methods. //! Substrate provides the `sc-rpc` crate, which defines the core RPC layer //! used by Substrate nodes. This file extends those RPC definitions with //! capabilities that are specific to this project's runtime configuration. #![warn(missing_docs)] use std::sync::Arc; use academy_pow_runtime::{opaque::Block, AccountId, Balance, Index}; use jsonrpsee::RpcModule; pub use sc_rpc_api::DenyUnsafe; use sc_transaction_pool_api::TransactionPool; use sp_api::ProvideRuntimeApi; use sp_block_builder::BlockBuilder; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; /// Full client dependencies. pub struct FullDeps<C, P> { /// The client instance to use. pub client: Arc<C>, /// Transaction pool instance. pub pool: Arc<P>, /// Whether to deny unsafe calls pub deny_unsafe: DenyUnsafe, } /// Instantiate all full RPC extensions. pub fn create_full<C, P>( deps: FullDeps<C, P>, ) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>> where C: ProvideRuntimeApi<Block>, C: HeaderBackend<Block> + HeaderMetadata<Block, Error = BlockChainError> + 'static, C: Send + Sync + 'static, C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>, C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>, C::Api: BlockBuilder<Block>, P: TransactionPool + 'static, { use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; use substrate_frame_rpc_system::{System, SystemApiServer}; let mut module = RpcModule::new(()); let FullDeps { client, pool, deny_unsafe } = deps; module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; module.merge(TransactionPayment::new(client).into_rpc())?; Ok(module) } }
#![allow(unused)] fn main() { //! Service and ServiceFactory implementation. Specialized wrapper over substrate service. use core::clone::Clone; use std::sync::Arc; use academy_pow_runtime::{self, opaque::Block, PreDigest, RuntimeApi}; use multi_pow::{ForkingConfig, MultiPow, SupportedHashes}; use parity_scale_codec::Encode; use sc_consensus::LongestChain; use sc_service::{error::Error as ServiceError, Configuration, PartialComponents, TaskManager}; use sc_telemetry::{Telemetry, TelemetryWorker}; use sp_core::sr25519; pub(crate) type FullClient = sc_service::TFullClient< Block, RuntimeApi, sc_executor::WasmExecutor<sp_io::SubstrateHostFunctions>, >; type FullBackend = sc_service::TFullBackend<Block>; type FullSelectChain = sc_consensus::LongestChain<FullBackend, Block>; type BasicImportQueue = sc_consensus::DefaultImportQueue<Block>; type BoxBlockImport = sc_consensus::BoxBlockImport<Block>; pub type Service = PartialComponents< FullClient, FullBackend, FullSelectChain, BasicImportQueue, sc_transaction_pool::FullPool<Block, FullClient>, (BoxBlockImport, Option<Telemetry>), >; /// Returns most parts of a service. Not enough to run a full chain, /// But enough to perform chain operations like purge-chain #[allow(clippy::type_complexity)] pub fn new_partial( config: &Configuration, fork_config: ForkingConfig, ) -> Result<Service, ServiceError> { let telemetry = config .telemetry_endpoints .clone() .filter(|x| !x.is_empty()) .map(|endpoints| -> Result<_, sc_telemetry::Error> { let worker = TelemetryWorker::new(16)?; let telemetry = worker.handle().new_telemetry(endpoints); Ok((worker, telemetry)) }) .transpose()?; let executor = sc_service::new_wasm_executor::<sp_io::SubstrateHostFunctions>(config); let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::<Block, RuntimeApi, _>( config, telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), executor, )?; let client = Arc::new(client); let telemetry = telemetry.map(|(worker, telemetry)| { task_manager.spawn_handle().spawn("telemetry", None, worker.run()); telemetry }); let select_chain = LongestChain::new(backend.clone()); let transaction_pool = sc_transaction_pool::BasicPool::new_full( config.transaction_pool.clone(), config.role.is_authority().into(), config.prometheus_registry(), task_manager.spawn_essential_handle(), client.clone(), ); let block_import = sc_consensus_pow::PowBlockImport::new( client.clone(), client.clone(), MultiPow::new(client.clone(), fork_config), 0, // check inherents starting at block 0 select_chain.clone(), move |_, ()| async move { let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); // We don't need the current mining key to check inherents, so we just use a default. // TODO, I don't think we need to do any checking here at all, right? // So can I just remove the author entirely? let author = academy_pow_runtime::block_author::InherentDataProvider(Default::default()); Ok((timestamp, author)) }, ); let import_queue = sc_consensus_pow::import_queue( Box::new(block_import.clone()), None, MultiPow::new(client.clone(), fork_config), &task_manager.spawn_essential_handle(), config.prometheus_registry(), )?; Ok(PartialComponents { client, backend, task_manager, import_queue, keystore_container, select_chain, transaction_pool, other: (Box::new(block_import), telemetry), }) } /// Builds a new service for a full client. pub fn new_full< N: sc_network::NetworkBackend<Block, <Block as sp_runtime::traits::Block>::Hash>, >( config: Configuration, fork_config: ForkingConfig, sr25519_public_key: sr25519::Public, instant_seal: bool, mining_algo: SupportedHashes, ) -> Result<TaskManager, ServiceError> { let sc_service::PartialComponents { client, backend, mut task_manager, import_queue, keystore_container, select_chain, transaction_pool, other: (pow_block_import, mut telemetry), } = new_partial(&config, fork_config)?; let net_config = sc_network::config::FullNetworkConfiguration::< Block, <Block as sp_runtime::traits::Block>::Hash, N, >::new(&config.network); let metrics = sc_network::NotificationMetrics::new(None); let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = sc_service::build_network(sc_service::BuildNetworkParams { config: &config, net_config, client: client.clone(), transaction_pool: transaction_pool.clone(), spawn_handle: task_manager.spawn_handle(), import_queue, block_announce_validator_builder: None, warp_sync_params: None, block_relay: None, metrics, })?; let role = config.role.clone(); let prometheus_registry = config.prometheus_registry().cloned(); let rpc_extensions_builder = { let client = client.clone(); let pool = transaction_pool.clone(); Box::new(move |deny_unsafe, _| { let deps = crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe }; crate::rpc::create_full(deps).map_err(Into::into) }) }; sc_service::spawn_tasks(sc_service::SpawnTasksParams { network, client: client.clone(), keystore: keystore_container.keystore(), task_manager: &mut task_manager, transaction_pool: transaction_pool.clone(), rpc_builder: rpc_extensions_builder, backend, system_rpc_tx, tx_handler_controller, sync_service: sync_service.clone(), config, telemetry: telemetry.as_mut(), })?; if role.is_authority() { let proposer = sc_basic_authorship::ProposerFactory::new( task_manager.spawn_handle(), client.clone(), transaction_pool.clone(), prometheus_registry.as_ref(), telemetry.as_ref().map(|x| x.handle()), ); // If instant seal is requested, we just start it. Otherwise, we do the full PoW setup. if instant_seal { let params = sc_consensus_manual_seal::InstantSealParams { block_import: client.clone(), env: proposer, client, pool: transaction_pool, select_chain, consensus_data_provider: None, create_inherent_data_providers: move |_, ()| async move { Ok(sp_timestamp::InherentDataProvider::from_system_time()) }, }; let authorship_future = sc_consensus_manual_seal::run_instant_seal(params); task_manager.spawn_essential_handle().spawn_blocking( "instant-seal", None, authorship_future, ); } else { let (mining_worker, mining_worker_task) = sc_consensus_pow::start_mining_worker( Box::new(pow_block_import), client.clone(), select_chain, MultiPow::new(client, fork_config), proposer, sync_service.clone(), sync_service, // Note the mining algorithm in the pre-runtime digest. // This allows us to know which algo it was in the runtime. // TODO This also makes it possible to remove the algo info from // the seal. Some(PreDigest::from((sr25519_public_key.into(), mining_algo)).encode()), // This code is copied from above. Would be better to not repeat it. move |_, ()| async move { let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); // set default `author` following miner specified in CLI let author = academy_pow_runtime::block_author::InherentDataProvider( sr25519_public_key.encode(), ); Ok((timestamp, author)) }, std::time::Duration::from_secs(10), std::time::Duration::from_secs(5), ); task_manager.spawn_essential_handle().spawn_blocking( "pow-miner", Some("pow-mining"), mining_worker_task, ); // Start Mining worker. //TODO Some of this should move into the multi_pow crate. use multi_pow::{multi_hash_meets_difficulty, Compute}; use sp_core::U256; let mut nonce: U256 = U256::from(0); std::thread::spawn(move || loop { let worker = mining_worker.clone(); let metadata = worker.metadata(); if let Some(metadata) = metadata { let compute = Compute { difficulty: metadata.difficulty, pre_hash: metadata.pre_hash, nonce, }; let seal = compute.compute(mining_algo); if multi_hash_meets_difficulty(&seal.work, seal.difficulty) { nonce = U256::from(0); let _ = futures::executor::block_on(worker.submit(seal.encode())); } else { nonce = nonce.saturating_add(U256::from(1)); if nonce == U256::MAX { nonce = U256::from(0); } } } else { std::thread::sleep(std::time::Duration::from_secs(1)); } }); } } network_starter.start_network(); Ok(task_manager) } }
fn main() { #[cfg(feature = "std")] substrate_wasm_builder::WasmBuilder::new() .with_current_project() .export_heap_base() .import_memory() .build() }
#![allow(unused)] fn main() { //! A difficulty adjustment algorithm (DAA) to keep the block time close to a particular goal //! Cribbed from Kulupu https://github.com/kulupu/kulupu/blob/master/runtime/src/difficulty.rs //! //! It is possible to implement other DAAs such as that of BTC and BCH. This would be an interesting //! and worth-while experiment. The DAAs should be abstracted away with a trait. //! Some ideas: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3410460 use core::cmp::{max, min}; use frame_support::traits::Time; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_core::U256; use sp_runtime::traits::UniqueSaturatedInto; #[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, Debug, MaxEncodedLen, TypeInfo)] pub struct DifficultyAndTimestamp<M> { pub difficulty: Difficulty, pub timestamp: M, } /// Move value linearly toward a goal pub fn damp(actual: u128, goal: u128, damp_factor: u128) -> u128 { (actual + (damp_factor - 1) * goal) / damp_factor } /// Limit value to be within some factor from a goal pub fn clamp(actual: u128, goal: u128, clamp_factor: u128) -> u128 { max(goal / clamp_factor, min(actual, goal * clamp_factor)) } const DIFFICULTY_ADJUST_WINDOW: u128 = 60; type Difficulty = U256; pub use pallet::*; #[frame_support::pallet(dev_mode)] pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use super::*; /// Pallet's configuration trait. #[pallet::config] pub trait Config<I: 'static = ()>: frame_system::Config { /// A Source for timestamp data type TimeProvider: Time; /// The block time that the DAA will attempt to maintain type TargetBlockTime: Get<u128>; /// Dampening factor to use for difficulty adjustment type DampFactor: Get<u128>; /// Clamp factor to use for difficulty adjustment /// Limit value to within this factor of goal. Recommended value: 2 type ClampFactor: Get<u128>; /// The maximum difficulty allowed. Recommended to use u128::max_value() type MaxDifficulty: Get<u128>; /// Minimum difficulty, enforced in difficulty retargetting /// avoids getting stuck when trying to increase difficulty subject to dampening /// Recommended to use same value as DampFactor type MinDifficulty: Get<u128>; /// Now that the pallet is instantiable, we need a way to decide which blocks are /// relevant to this instance. This function does just that. /// /// The default implementation assumes that all blocks are relevant which is what /// you probably want when there is only a single instance. fn relevant_to_this_instance() -> bool { true } } #[pallet::pallet] pub struct Pallet<T, I = ()>(_); type DifficultyList<T, I> = [Option<DifficultyAndTimestamp<<<T as Config<I>>::TimeProvider as Time>::Moment>>; 60]; /// Past difficulties and timestamps, from earliest to latest. #[pallet::storage] pub type PastDifficultiesAndTimestamps<T: Config<I>, I: 'static = ()> = StorageValue<_, DifficultyList<T, I>, ValueQuery, EmptyList<T, I>>; pub struct EmptyList<T: Config<I>, I: 'static = ()>(PhantomData<(T, I)>); impl<T: Config<I>, I> Get<DifficultyList<T, I>> for EmptyList<T, I> { fn get() -> DifficultyList<T, I> { [None; DIFFICULTY_ADJUST_WINDOW as usize] } } /// Current difficulty. #[pallet::storage] #[pallet::getter(fn difficulty)] pub type CurrentDifficulty<T: Config<I>, I: 'static = ()> = StorageValue<_, Difficulty, ValueQuery>; /// Initial difficulty. #[pallet::storage] pub type InitialDifficulty<T: Config<I>, I: 'static = ()> = StorageValue<_, Difficulty, ValueQuery>; #[pallet::genesis_config] pub struct GenesisConfig<T: Config<I>, I: 'static = ()> { pub _ph_data: Option<PhantomData<(T, I)>>, pub initial_difficulty: [u8; 32], // Difficulty = U256 } #[pallet::genesis_build] impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> { fn build(&self) { let initial_difficulty = U256::from_little_endian(&self.initial_difficulty); // Initialize the Current difficulty CurrentDifficulty::<T, I>::put(&initial_difficulty); // Store the initial difficulty in storage because we will need it // during the first DIFFICULTY_ADJUSTMENT_WINDOW blocks (see todo below). InitialDifficulty::<T, I>::put(&initial_difficulty); } } impl<T: Config<I>, I: 'static> Default for GenesisConfig<T, I> { fn default() -> Self { GenesisConfig { _ph_data: Default::default(), initial_difficulty: [0u8; 32] } } } #[pallet::hooks] impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> { fn on_finalize(_n: BlockNumberFor<T>) { // First check if this is block is relevant to this instance of the difficulty // adjustment algorithm if !T::relevant_to_this_instance() { return; } let mut data = PastDifficultiesAndTimestamps::<T, I>::get(); for i in 1..data.len() { data[i - 1] = data[i]; } data[data.len() - 1] = Some(DifficultyAndTimestamp { timestamp: T::TimeProvider::now(), difficulty: Self::difficulty(), }); let mut ts_delta = 0; for i in 1..(DIFFICULTY_ADJUST_WINDOW as usize) { let prev: Option<u128> = data[i - 1].map(|d| d.timestamp.unique_saturated_into()); let cur: Option<u128> = data[i].map(|d| d.timestamp.unique_saturated_into()); let delta = match (prev, cur) { (Some(prev), Some(cur)) => cur.saturating_sub(prev), _ => T::TargetBlockTime::get(), }; ts_delta += delta; } if ts_delta == 0 { ts_delta = 1; } let mut diff_sum = U256::zero(); //TODO Could we just initialize every array cell to the initial difficulty to not need // the separate storage item? for item in data.iter().take(DIFFICULTY_ADJUST_WINDOW as usize) { let diff = match item.map(|d| d.difficulty) { Some(diff) => diff, None => InitialDifficulty::<T, I>::get(), }; diff_sum += diff; } if diff_sum < U256::from(T::MinDifficulty::get()) { diff_sum = U256::from(T::MinDifficulty::get()); } // Calculate the average length of the adjustment window let adjustment_window = DIFFICULTY_ADJUST_WINDOW * T::TargetBlockTime::get(); // adjust time delta toward goal subject to dampening and clamping let adj_ts = clamp( damp(ts_delta, adjustment_window, T::DampFactor::get()), adjustment_window, T::ClampFactor::get(), ); // minimum difficulty avoids getting stuck due to dampening let difficulty = min( U256::from(T::MaxDifficulty::get()), max( U256::from(T::MinDifficulty::get()), diff_sum * U256::from(T::TargetBlockTime::get()) / U256::from(adj_ts), ), ); <PastDifficultiesAndTimestamps<T, I>>::put(data); <CurrentDifficulty<T, I>>::put(difficulty); } } } }
#![allow(unused)] #![cfg_attr(not(feature = "std"), no_std)] fn main() { /// A trait for types that can provide the amount of issuance to award to the block /// author for the given block number. pub trait Issuance<BlockNumber, Balance> { fn issuance(block: BlockNumber) -> Balance; } // Minimal implementations for when you don't actually want any issuance impl Issuance<u32, u128> for () { fn issuance(_block: u32) -> u128 { 0 } } impl Issuance<u64, u128> for () { fn issuance(_block: u64) -> u128 { 0 } } /// A type that provides block issuance according to bitcoin's rules /// Initial issuance is 50 / block /// Issuance is cut in half every 210,000 blocks /// cribbed from /// github.com/Bitcoin-ABC/bitcoin-abc/blob/9c7b12e6f128a59423f4de3d6d4b5231ebe9aac2/src/validation. /// cpp#L1007 pub struct BitcoinHalving; /// The number of blocks between each halving. const HALVING_INTERVAL: u32 = 210_000; /// The per-block issuance before any halving. Decimal places should be accounted for here. const INITIAL_ISSUANCE: u32 = 50; impl Issuance<u32, u128> for BitcoinHalving { fn issuance(block: u32) -> u128 { let halving = block / HALVING_INTERVAL; // Force block reward to zero when right shift is undefined. if halving >= 64 { return 0; } // Approximately, 600 seconds (or 10 minutes) for a block to be finalized. // Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years. // Divided by 2 using bitwise (INITIAL_ISSUANCE >> halving).into() } } }
#![allow(unused)] fn main() { //! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm. #![cfg_attr(not(feature = "std"), no_std)] // The construct runtime macro does a lot of recursion and requires us to increase the limit to 256. #![recursion_limit = "256"] // Make the WASM binary available. #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); pub use frame_support::{ construct_runtime, derive_impl, parameter_types, traits::{ Currency, EstimateNextNewSession, Imbalance, IsSubType, KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, ValidatorSet, VariantCountOf, }, weights::{ constants::{ BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, }, IdentityFee, Weight, }, Callable, StorageValue, }; use frame_support::{ genesis_builder_helper::{build_state, get_preset}, instances::{Instance1, Instance2, Instance3}, sp_runtime::Perquintill, traits::{ConstU128, ConstU32, ConstU8}, }; use multi_pow::SupportedHashes; pub use pallet_balances::Call as BalancesCall; pub use pallet_timestamp::Call as TimestampCall; use pallet_transaction_payment::{ConstFeeMultiplier, FungibleAdapter, Multiplier}; use parity_scale_codec::Decode; use sp_api::impl_runtime_apis; use sp_consensus_pow::POW_ENGINE_ID; use sp_core::OpaqueMetadata; // A few exports that help ease life for downstream crates. #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; use sp_runtime::{ create_runtime_str, generic, traits::{ AccountIdLookup, BlakeTwo256, Block as BlockT, Bounded, IdentifyAccount, One, Verify, }, transaction_validity::{ InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, }, ApplyExtrinsicResult, DigestItem, ExtrinsicInclusionMode, MultiSignature, }; pub use sp_runtime::{FixedPointNumber, Perbill, Permill}; use sp_std::prelude::*; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; /// An index to a block. pub type BlockNumber = u32; /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. pub type Signature = MultiSignature; /// Some way of identifying an account on the chain. We intentionally make it equivalent /// to the public key of our transaction signing scheme. pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId; /// The type for looking up accounts. We don't expect more than 4 billion of them, but you /// never know... pub type AccountIndex = u32; /// Balance of an account. pub type Balance = u128; /// Index of a transaction in the chain. pub type Nonce = u32; /// Index of a transaction in the chain. pub type Index = u32; /// A hash of some data used by the chain. pub type Hash = sp_core::H256; /// Consensus digest containing block author and supported hash algorithm. pub type PreDigest = (AccountId, SupportedHashes); /// The BlockAuthor trait in `./block_author.rs` pub mod block_author; // /// The Difficulty Adjustment Algorithm in `./difficulty.rs` pub mod difficulty; /// The total issuance and halving time pub mod issuance; /// UTXOs serve as the digital equivalent of change you receive after making a cash purchase pub mod utxo; /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know /// the specifics of the runtime. They can then be made to be agnostic over specific formats /// of data like extrinsics, allowing for them to continue syncing the network through upgrades /// to even the core data structures. pub mod opaque { pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; use super::*; /// Opaque block header type. pub type Header = generic::Header<BlockNumber, BlakeTwo256>; /// Opaque block type. pub type Block = generic::Block<Header, UncheckedExtrinsic>; /// Opaque block identifier type. pub type BlockId = generic::BlockId<Block>; } /// This runtime version. #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("academy-pow"), impl_name: create_runtime_str!("academy-pow"), authoring_version: 1, spec_version: 1, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, state_version: 1, }; /// The version information used to identify this runtime when compiled natively. #[cfg(feature = "std")] pub fn native_version() -> NativeVersion { NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } } const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); // native chain currency pub const TOKEN_SYMBOL: &str = "Unit"; pub const TOKEN_DECIMALS: u32 = 12; pub const TOKEN: u128 = 10u128.pow(TOKEN_DECIMALS); parameter_types! { pub const BlockHashCount: BlockNumber = 2400; pub const Version: RuntimeVersion = VERSION; /// We allow for 2 seconds of compute with a 6 second average block time. pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::with_sensible_defaults( Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), NORMAL_DISPATCH_RATIO, ); pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); pub const SS58Prefix: u8 = 42; } #[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)] impl frame_system::Config for Runtime { /// The basic call filter to use in dispatchable. type BaseCallFilter = frame_support::traits::Everything; /// Block & extrinsics weights: base values and limits. type BlockWeights = BlockWeights; /// The maximum length of a block (in bytes). type BlockLength = BlockLength; /// The identifier used to distinguish between accounts. type AccountId = AccountId; /// The aggregated dispatch type that is available for extrinsics. type RuntimeCall = RuntimeCall; /// The lookup mechanism to get account ID from whatever is passed in dispatchers. type Lookup = AccountIdLookup<AccountId, ()>; /// The type for hashing blocks and tries. type Hash = Hash; /// The hashing algorithm used. type Hashing = BlakeTwo256; /// The ubiquitous event type. type RuntimeEvent = RuntimeEvent; /// The ubiquitous origin type. type RuntimeOrigin = RuntimeOrigin; /// Maximum number of block number to block hash mappings to keep (oldest pruned first). type BlockHashCount = BlockHashCount; /// The weight of database operations that the runtime can invoke. type DbWeight = RocksDbWeight; /// Version of the runtime. type Version = Version; /// This type is being generated by the construct runtime macro. type PalletInfo = PalletInfo; /// What to do if a new account is created. type OnNewAccount = (); /// What to do if an account is fully reaped from the system. type OnKilledAccount = (); /// The data to be stored in an account. type AccountData = pallet_balances::AccountData<Balance>; /// Weight information for the extrinsics of this pallet. type SystemWeightInfo = (); /// This is used as an identifier of the chain. 42 is the generic substrate prefix. type SS58Prefix = SS58Prefix; /// The set code logic, just the default since we're not a parachain. type OnSetCode = (); type MaxConsumers = frame_support::traits::ConstU32<16>; type Nonce = Nonce; type Block = Block; } parameter_types! { pub const MinimumPeriod: u64 = 1000; } impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = (); type MinimumPeriod = MinimumPeriod; type WeightInfo = (); } impl pallet_balances::Config for Runtime { type MaxLocks = ConstU32<50>; type MaxReserves = (); type ReserveIdentifier = [u8; 8]; /// The type for recording an account's balance. type Balance = Balance; /// The ubiquitous event type. type RuntimeEvent = RuntimeEvent; type DustRemoval = (); type ExistentialDeposit = ConstU128<500>; type AccountStore = System; type WeightInfo = pallet_balances::weights::SubstrateWeight<Runtime>; type FreezeIdentifier = RuntimeFreezeReason; type MaxFreezes = VariantCountOf<RuntimeFreezeReason>; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; } parameter_types! { pub const TargetBlockTime: u128 = 5_000; // Setting min difficulty to damp factor per recommendation pub const DampFactor: u128 = 3; pub const ClampFactor: u128 = 2; pub const MaxDifficulty: u128 = u128::max_value(); } // Helper function to get the current blocks PoW algo from the predigest fn current_blocks_mining_algo() -> SupportedHashes { System::digest() .logs .iter() .find_map(|digest_item| match digest_item { DigestItem::PreRuntime(POW_ENGINE_ID, pre_digest) => PreDigest::decode(&mut &pre_digest[..]).map(|d| d.1).ok(), _ => None, }) .expect("There should be exactly one pow pre-digest item") } impl difficulty::Config<Instance1> for Runtime { type TimeProvider = Timestamp; type TargetBlockTime = TargetBlockTime; type DampFactor = DampFactor; type ClampFactor = ClampFactor; type MaxDifficulty = MaxDifficulty; type MinDifficulty = DampFactor; fn relevant_to_this_instance() -> bool { current_blocks_mining_algo() == SupportedHashes::Md5 } } impl difficulty::Config<Instance2> for Runtime { type TimeProvider = Timestamp; type TargetBlockTime = TargetBlockTime; type DampFactor = DampFactor; type ClampFactor = ClampFactor; type MaxDifficulty = MaxDifficulty; type MinDifficulty = DampFactor; fn relevant_to_this_instance() -> bool { current_blocks_mining_algo() == SupportedHashes::Sha3 } } impl difficulty::Config<Instance3> for Runtime { type TimeProvider = Timestamp; type TargetBlockTime = TargetBlockTime; type DampFactor = DampFactor; type ClampFactor = ClampFactor; type MaxDifficulty = MaxDifficulty; type MinDifficulty = DampFactor; fn relevant_to_this_instance() -> bool { current_blocks_mining_algo() == SupportedHashes::Keccak } } impl block_author::Config for Runtime { // Each block mined issues 50 new tokens to the miner fn on_author_set(author_account: Self::AccountId) { let issuance = 50 * TOKEN; let _ = Balances::deposit_creating(&author_account, issuance); } } parameter_types! { // This value increases the priority of `Operational` transactions by adding // a "virtual tip" that's equal to the `OperationalFeeMultiplier * final_fee`. // follows polkadot : https://github.com/paritytech/polkadot/blob/9ce5f7ef5abb1a4291454e8c9911b304d80679f9/runtime/polkadot/src/lib.rs#L369 pub const OperationalFeeMultiplier: u8 = 5; // We expect that on average 25% of the normal capacity will be occupied with normal txs. pub const TargetSaturationLevel: Perquintill = Perquintill::from_percent(25); // During 20 blocks the fee may not change more than by 100%. This, together with the // `TargetSaturationLevel` value, results in variability ~0.067. For the corresponding // formulas please refer to Substrate code at `frame/transaction-payment/src/lib.rs`. pub FeeVariability: Multiplier = Multiplier::saturating_from_rational(67, 1000); // Fee should never be lower than the computational cost. pub MinimumMultiplier: Multiplier = Multiplier::one(); pub MaximumMultiplier: Multiplier = Bounded::max_value(); } parameter_types! { pub FeeMultiplier: Multiplier = Multiplier::one(); } impl pallet_transaction_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = FungibleAdapter<Balances, ()>; type OperationalFeeMultiplier = ConstU8<5>; type WeightToFee = IdentityFee<Balance>; type LengthToFee = IdentityFee<Balance>; type FeeMultiplierUpdate = ConstFeeMultiplier<FeeMultiplier>; } construct_runtime!( pub struct Runtime { System: frame_system, Timestamp: pallet_timestamp, Balances: pallet_balances, TransactionPayment: pallet_transaction_payment, Md5DifficultyAdjustment: difficulty::<Instance1>, Sha3DifficultyAdjustment: difficulty::<Instance2>, KeccakDifficultyAdjustment: difficulty::<Instance3>, BlockAuthor: block_author, } ); /// The address format for describing accounts. pub type Address = sp_runtime::MultiAddress<AccountId, ()>; /// Block header type as expected by this runtime. pub type Header = generic::Header<BlockNumber, BlakeTwo256>; /// Block type as expected by this runtime. pub type Block = generic::Block<Header, UncheckedExtrinsic>; /// A Block signed with a Justification pub type SignedBlock = generic::SignedBlock<Block>; /// The SignedExtension to the basic transaction logic. pub type SignedExtra = ( frame_system::CheckNonZeroSender<Runtime>, frame_system::CheckSpecVersion<Runtime>, frame_system::CheckTxVersion<Runtime>, frame_system::CheckGenesis<Runtime>, frame_system::CheckEra<Runtime>, frame_system::CheckNonce<Runtime>, frame_system::CheckWeight<Runtime>, pallet_transaction_payment::ChargeTransactionPayment<Runtime>, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, Block, frame_system::ChainContext<Runtime>, Runtime, AllPalletsWithSystem, >; impl_runtime_apis! { impl sp_api::Core<Block> for Runtime { fn version() -> RuntimeVersion { VERSION } fn execute_block(block: Block) { Executive::execute_block(block) } fn initialize_block(header: &<Block as BlockT>::Header) -> ExtrinsicInclusionMode { Executive::initialize_block(header) } } impl sp_api::Metadata<Block> for Runtime { fn metadata() -> OpaqueMetadata { OpaqueMetadata::new(Runtime::metadata().into()) } fn metadata_at_version(version: u32) -> Option<OpaqueMetadata> { Runtime::metadata_at_version(version) } fn metadata_versions() -> sp_std::vec::Vec<u32> { Runtime::metadata_versions() } } impl sp_block_builder::BlockBuilder<Block> for Runtime { fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult { Executive::apply_extrinsic(extrinsic) } fn finalize_block() -> <Block as BlockT>::Header { Executive::finalize_block() } fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> { data.create_extrinsics() } fn check_inherents( block: Block, data: sp_inherents::InherentData, ) -> sp_inherents::CheckInherentsResult { data.check_extrinsics(&block) } } impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime { fn validate_transaction( source: TransactionSource, tx: <Block as BlockT>::Extrinsic, block_hash: <Block as BlockT>::Hash, ) -> TransactionValidity { Executive::validate_transaction(source, tx, block_hash) } } impl sp_offchain::OffchainWorkerApi<Block> for Runtime { fn offchain_worker(header: &<Block as BlockT>::Header) { Executive::offchain_worker(header) } } impl sp_session::SessionKeys<Block> for Runtime { fn generate_session_keys(_seed: Option<Vec<u8>>) -> Vec<u8> { Vec::new() } fn decode_session_keys( _encoded: Vec<u8>, ) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> { None } } impl sp_consensus_pow::DifficultyApi<Block, multi_pow::Threshold> for Runtime { fn difficulty() -> multi_pow::Threshold { multi_pow::Threshold { md5: Md5DifficultyAdjustment::difficulty(), sha3: Sha3DifficultyAdjustment::difficulty(), keccak: KeccakDifficultyAdjustment::difficulty(), } } } impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime { fn account_nonce(account: AccountId) -> Index { System::account_nonce(account) } } impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime { fn query_info( uxt: <Block as BlockT>::Extrinsic, len: u32, ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> { TransactionPayment::query_info(uxt, len) } fn query_fee_details( uxt: <Block as BlockT>::Extrinsic, len: u32, ) -> pallet_transaction_payment::FeeDetails<Balance> { TransactionPayment::query_fee_details(uxt, len) } fn query_weight_to_fee(weight: Weight) -> Balance { TransactionPayment::weight_to_fee(weight) } fn query_length_to_fee(length: u32) -> Balance { TransactionPayment::length_to_fee(length) } } impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi<Block, Balance, RuntimeCall> for Runtime { fn query_call_info( call: RuntimeCall, len: u32, ) -> pallet_transaction_payment::RuntimeDispatchInfo<Balance> { TransactionPayment::query_call_info(call, len) } fn query_call_fee_details( call: RuntimeCall, len: u32, ) -> pallet_transaction_payment::FeeDetails<Balance> { TransactionPayment::query_call_fee_details(call, len) } fn query_weight_to_fee(weight: Weight) -> Balance { TransactionPayment::weight_to_fee(weight) } fn query_length_to_fee(length: u32) -> Balance { TransactionPayment::length_to_fee(length) } } impl sp_genesis_builder::GenesisBuilder<Block> for Runtime { fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result { build_state::<RuntimeGenesisConfig>(config) } fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> { get_preset::<RuntimeGenesisConfig>(id, |_| None) } fn preset_names() -> Vec<sp_genesis_builder::PresetId> { Default::default() } } } }
#![allow(unused)] fn main() { // We make sure this pallet uses `no_std` for compiling to Wasm. #![cfg_attr(not(feature = "std"), no_std)] use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_core::{ sr25519::{Public, Signature}, ByteArray, H256, H512, }; use sp_runtime::traits::{BlakeTwo256, Hash, SaturatedConversion}; use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; use super::{block_author::BlockAuthor, issuance::Issuance}; pub use pallet::*; #[frame_support::pallet(dev_mode)] pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use super::*; #[pallet::config] pub trait Config: frame_system::Config {} #[pallet::pallet] pub struct Pallet<T>(_); } }
# Basic
edition = "2021"
hard_tabs = true
max_width = 100
use_small_heuristics = "Max"
# Imports
imports_granularity = "Crate"
reorder_imports = true
# Consistency
newline_style = "Unix"
# 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
# Format comments
comment_width = 100
wrap_comments = true
diff --git a/multi-pow/src/lib.rs b/multi-pow/src/lib.rs
index 8c36e58..d3c0d24 100644
--- a/multi-pow/src/lib.rs
+++ b/multi-pow/src/lib.rs
@@ -3,16 +3,16 @@
//! It is multi-pow in the sense that there are multiple supported hashing algorithms.
//! A seal with any of the supported hashing algorithms will be accepted.
//!
-//! The purpose of this design is to demonstrate hard and soft forks by adding and removing valid hashing algorithms.
-//! While there is no precedent for changing hashing algorithms in the real world yet, it is conceivable that
-//! a chain may want to upgrade to a new algorithm when the old one is suspected weak.
-//! In any case, the point is that we want to demonstrate hard and soft forks in an understandable way,
-//! the multiple hashing algorithms achieves that well.
+//! The purpose of this design is to demonstrate hard and soft forks by adding and removing valid
+//! hashing algorithms. While there is no precedent for changing hashing algorithms in the real
+//! world yet, it is conceivable that a chain may want to upgrade to a new algorithm when the old
+//! one is suspected weak. In any case, the point is that we want to demonstrate hard and soft forks
+//! in an understandable way, the multiple hashing algorithms achieves that well.
//!
-//! In the future, the hope is that there will be a dedicated difficulty threshold for each hashing algorithm.
-//! But currently the Substrate PoW crates are not that flexible.
-//! We could solve it by adding a pre-digest that includes information about what hashing algo is being used
-//! for the runtime to use later in the difficulty adjustment.
+//! In the future, the hope is that there will be a dedicated difficulty threshold for each hashing
+//! algorithm. But currently the Substrate PoW crates are not that flexible.
+//! We could solve it by adding a pre-digest that includes information about what hashing algo is
+//! being used for the runtime to use later in the difficulty adjustment.
#![cfg_attr(not(feature = "std"), no_std)]
@@ -41,55 +41,55 @@ use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
/// A struct that represents a difficulty threshold.
/// Unlike a normal PoW algorithm this struct has a separate threshold for each hash
#[derive(
- Clone,
- Copy,
- PartialEq,
- Eq,
- PartialOrd,
- Ord,
- Encode,
- Decode,
- Debug,
- Default,
- scale_info::TypeInfo,
+ Clone,
+ Copy,
+ PartialEq,
+ Eq,
+ PartialOrd,
+ Ord,
+ Encode,
+ Decode,
+ Debug,
+ Default,
+ scale_info::TypeInfo,
)]
pub struct Threshold {
- pub md5: U256,
- pub sha3: U256,
- pub keccak: U256,
+ pub md5: U256,
+ pub sha3: U256,
+ pub keccak: U256,
}
// This trait does not seem to be fully baked in the Substrate PoW code
// But we do need some kind of sinsible impl here so the node can import blocks.
// so I will not use it for now.
impl TotalDifficulty for Threshold {
- fn increment(&mut self, other: Threshold) {
- self.md5 += other.md5;
- self.sha3 += other.sha3;
- self.keccak += other.keccak;
- }
+ fn increment(&mut self, other: Threshold) {
+ self.md5 += other.md5;
+ self.sha3 += other.sha3;
+ self.keccak += other.keccak;
+ }
}
/// An enum that represents the supported hash types
#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, Debug)]
pub enum SupportedHashes {
- Md5,
- Sha3,
- Keccak,
+ Md5,
+ Sha3,
+ Keccak,
}
impl Default for SupportedHashes {
- fn default() -> Self {
- Self::Sha3
- }
+ fn default() -> Self {
+ Self::Sha3
+ }
}
/// A struct that represents a concrete hash value tagged with what hashing
/// algorithm was used to compute it.
#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, Debug, Default)]
pub struct MultiHash {
- pub algo: SupportedHashes,
- pub value: H256,
+ pub algo: SupportedHashes,
+ pub value: H256,
}
/// Determine whether the given hash satisfies the given difficulty.
@@ -97,325 +97,310 @@ pub struct MultiHash {
/// overflows the bounds of U256, then the product (and thus the hash)
/// was too high.
pub fn simple_hash_meets_difficulty(hash: &H256, difficulty: U256) -> bool {
- let num_hash = U256::from_little_endian(&hash[..]);
- let (_, overflowed) = num_hash.overflowing_mul(difficulty);
+ let num_hash = U256::from_little_endian(&hash[..]);
+ let (_, overflowed) = num_hash.overflowing_mul(difficulty);
- !overflowed
+ !overflowed
}
pub fn multi_hash_meets_difficulty(hash: &MultiHash, difficulty: Threshold) -> bool {
- match hash.algo {
- SupportedHashes::Md5 => simple_hash_meets_difficulty(&hash.value, difficulty.md5),
- SupportedHashes::Sha3 => simple_hash_meets_difficulty(&hash.value, difficulty.sha3),
- SupportedHashes::Keccak => simple_hash_meets_difficulty(&hash.value, difficulty.keccak),
- }
+ match hash.algo {
+ SupportedHashes::Md5 => simple_hash_meets_difficulty(&hash.value, difficulty.md5),
+ SupportedHashes::Sha3 => simple_hash_meets_difficulty(&hash.value, difficulty.sha3),
+ SupportedHashes::Keccak => simple_hash_meets_difficulty(&hash.value, difficulty.keccak),
+ }
}
/// A Seal struct that will be encoded to a Vec<u8> as used as the
/// `RawSeal` type.
#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)]
pub struct Seal {
- pub work: MultiHash,
- pub difficulty: Threshold,
- pub nonce: U256,
+ pub work: MultiHash,
+ pub difficulty: Threshold,
+ pub nonce: U256,
}
/// A not-yet-computed attempt to solve the proof of work. Calling the
/// compute method will compute the hash and return the seal.
#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)]
pub struct Compute {
- pub difficulty: Threshold,
- pub pre_hash: H256,
- pub nonce: U256,
+ pub difficulty: Threshold,
+ pub pre_hash: H256,
+ pub nonce: U256,
}
#[cfg(feature = "std")]
impl Compute {
- pub fn compute(self, algo: SupportedHashes) -> Seal {
- let value = match algo {
- SupportedHashes::Md5 => {
- // The md5 is only 16 byte output, so we just concatenate it twice to
- // get an H256
- let bytes = *md5::compute(&self.encode()[..]);
- let mut doubled = [0u8; 32];
- doubled[0..16].copy_from_slice(&bytes[0..16]);
- doubled[16..32].copy_from_slice(&bytes[0..16]);
-
- H256::from(doubled)
- }
- SupportedHashes::Sha3 => {
- H256::from_slice(Sha3_256::digest(&self.encode()[..]).as_slice())
- }
- SupportedHashes::Keccak => {
- H256::from_slice(Keccak256::digest(&self.encode()[..]).as_slice())
- }
- };
-
- Seal {
- nonce: self.nonce,
- difficulty: self.difficulty,
- work: MultiHash { algo, value },
- }
- }
+ pub fn compute(self, algo: SupportedHashes) -> Seal {
+ let value = match algo {
+ SupportedHashes::Md5 => {
+ // The md5 is only 16 byte output, so we just concatenate it twice to
+ // get an H256
+ let bytes = *md5::compute(&self.encode()[..]);
+ let mut doubled = [0u8; 32];
+ doubled[0..16].copy_from_slice(&bytes[0..16]);
+ doubled[16..32].copy_from_slice(&bytes[0..16]);
+
+ H256::from(doubled)
+ },
+ SupportedHashes::Sha3 =>
+ H256::from_slice(Sha3_256::digest(&self.encode()[..]).as_slice()),
+ SupportedHashes::Keccak =>
+ H256::from_slice(Keccak256::digest(&self.encode()[..]).as_slice()),
+ };
+
+ Seal { nonce: self.nonce, difficulty: self.difficulty, work: MultiHash { algo, value } }
+ }
}
#[cfg(feature = "std")]
/// A complete PoW Algorithm that uses multiple hashing algorithms.
/// Needs a reference to the client so it can grab the difficulty from the runtime.
pub struct MultiPow<C> {
- client: Arc<C>,
- fork_config: ForkingConfig,
+ client: Arc<C>,
+ fork_config: ForkingConfig,
}
#[cfg(feature = "std")]
impl<C> MultiPow<C> {
- pub fn new(client: Arc<C>, fork_config: ForkingConfig) -> Self {
- Self {
- client,
- fork_config,
- }
- }
+ pub fn new(client: Arc<C>, fork_config: ForkingConfig) -> Self {
+ Self { client, fork_config }
+ }
}
//TODO could maybe derive clone_no_bound
#[cfg(feature = "std")]
impl<C> Clone for MultiPow<C> {
- fn clone(&self) -> Self {
- Self::new(self.client.clone(), self.fork_config)
- }
+ fn clone(&self) -> Self {
+ Self::new(self.client.clone(), self.fork_config)
+ }
}
// Here we implement the general PowAlgorithm trait for our concrete algorithm.
#[cfg(feature = "std")]
impl<B: BlockT<Hash = H256>, C> PowAlgorithm<B> for MultiPow<C>
where
- C: ProvideRuntimeApi<B>,
- C::Api: DifficultyApi<B, Threshold>,
- C: sc_client_api::HeaderBackend<B>,
+ C: ProvideRuntimeApi<B>,
+ C::Api: DifficultyApi<B, Threshold>,
+ C: sc_client_api::HeaderBackend<B>,
{
- type Difficulty = Threshold;
-
- fn difficulty(&self, parent: B::Hash) -> Result<Self::Difficulty, Error<B>> {
- let difficulty = self
- .client
- .runtime_api()
- .difficulty(parent)
- .map_err(|err| {
- sc_consensus_pow::Error::Environment(format!(
- "Fetching difficulty from runtime failed: {:?}",
- err
- ))
- })?;
-
- Ok(difficulty)
- }
-
- fn verify(
- &self,
- parent_id: &BlockId<B>,
- pre_hash: &H256,
- pre_digest: Option<&[u8]>,
- seal: &RawSeal,
- difficulty: Self::Difficulty,
- ) -> Result<bool, Error<B>> {
- // Try to construct a seal object by decoding the raw seal given
- let seal = match Seal::decode(&mut &seal[..]) {
- Ok(seal) => seal,
- Err(_) => return Ok(false),
- };
-
- log::debug!("✅ Decode seal passed!");
-
- let Some(_encoded_pre_digest) = pre_digest else {
- return Ok(false);
- };
-
- log::debug!("✅ Checksum digest passed!");
-
- // // TODO idk why this always return md5 only
- // let algo_from_predigest = match SupportedHashes::decode(&mut &encoded_pre_digest[..]) {
- // Ok(algo) => algo,
- // Err(_) => return Ok(false),
- // };
-
- // log::debug!("✅ Get algorithm from digest passed!");
-
- // // Check that the pre-digest algo matches the seal algo
- // // TODO it shouldn't be necessary to have both.
- // if seal.work.algo != algo_from_predigest {
- // return Ok(false);
- // }
-
- // log::debug!("✅ Checksum algorithm from seal passed!");
-
- // This is where we handle forks on the verification side.
- // We will still need to handle it in the mining algorithm somewhere.
- // Currently we make the miner configure what algo they mine manually with their cli.
- let parent_number: u32 = match parent_id {
- BlockId::Hash(h) => *self
- .client
- .header(*h)
- .expect("Database should perform lookup successfully")
- .expect("parent header should be present in the db")
- .number(),
- BlockId::Number(n) => *n,
- }
- .try_into()
- .map_err(|_| ())
- .expect("Block numbers can be converted to u32 (because they are u32)");
-
- log::debug!("✅ Checksum parent block number passed!");
-
- // Here we handle the forking logic according the the node operator's request.
- let valid_algorithm = match self.fork_config {
- ForkingConfig::Manual => manual_fork_validation(parent_number, seal.work.algo),
- ForkingConfig::Automatic(fork_heights, maxi_position) => {
- auto_fork_validation(parent_number, seal.work.algo, fork_heights, maxi_position)
- }
- };
-
- if !valid_algorithm {
- return Ok(false);
- }
-
- log::debug!("✅ Valid algorithm!");
-
- // See whether the hash meets the difficulty requirement. If not, fail fast.
- if !multi_hash_meets_difficulty(&seal.work, difficulty) {
- return Ok(false);
- }
-
- log::debug!("✅ Checksum difficulty passed!");
-
- // Make sure the provided work actually comes from the correct pre_hash
- let compute = Compute {
- difficulty,
- pre_hash: *pre_hash,
- nonce: seal.nonce,
- };
-
- if compute.compute(seal.work.algo) != seal {
- return Ok(false);
- }
-
- log::debug!("✅ Re-compute passed!");
-
- log::debug!("🛠️ All passed, append the block to the chain ...");
-
- Ok(true)
- }
+ type Difficulty = Threshold;
+
+ fn difficulty(&self, parent: B::Hash) -> Result<Self::Difficulty, Error<B>> {
+ let difficulty = self.client.runtime_api().difficulty(parent).map_err(|err| {
+ sc_consensus_pow::Error::Environment(format!(
+ "Fetching difficulty from runtime failed: {:?}",
+ err
+ ))
+ })?;
+
+ Ok(difficulty)
+ }
+
+ fn verify(
+ &self,
+ parent_id: &BlockId<B>,
+ pre_hash: &H256,
+ pre_digest: Option<&[u8]>,
+ seal: &RawSeal,
+ difficulty: Self::Difficulty,
+ ) -> Result<bool, Error<B>> {
+ // Try to construct a seal object by decoding the raw seal given
+ let seal = match Seal::decode(&mut &seal[..]) {
+ Ok(seal) => seal,
+ Err(_) => return Ok(false),
+ };
+
+ log::debug!("✅ Decode seal passed!");
+
+ let Some(_encoded_pre_digest) = pre_digest else {
+ return Ok(false);
+ };
+
+ log::debug!("✅ Checksum digest passed!");
+
+ // // TODO idk why this always return md5 only
+ // let algo_from_predigest = match SupportedHashes::decode(&mut &encoded_pre_digest[..]) {
+ // Ok(algo) => algo,
+ // Err(_) => return Ok(false),
+ // };
+
+ // log::debug!("✅ Get algorithm from digest passed!");
+
+ // // Check that the pre-digest algo matches the seal algo
+ // // TODO it shouldn't be necessary to have both.
+ // if seal.work.algo != algo_from_predigest {
+ // return Ok(false);
+ // }
+
+ // log::debug!("✅ Checksum algorithm from seal passed!");
+
+ // This is where we handle forks on the verification side.
+ // We will still need to handle it in the mining algorithm somewhere.
+ // Currently we make the miner configure what algo they mine manually with their cli.
+ let parent_number: u32 = match parent_id {
+ BlockId::Hash(h) => *self
+ .client
+ .header(*h)
+ .expect("Database should perform lookup successfully")
+ .expect("parent header should be present in the db")
+ .number(),
+ BlockId::Number(n) => *n,
+ }
+ .try_into()
+ .map_err(|_| ())
+ .expect("Block numbers can be converted to u32 (because they are u32)");
+
+ log::debug!("✅ Checksum parent block number passed!");
+
+ // Here we handle the forking logic according the the node operator's request.
+ let valid_algorithm = match self.fork_config {
+ ForkingConfig::Manual => manual_fork_validation(parent_number, seal.work.algo),
+ ForkingConfig::Automatic(fork_heights, maxi_position) =>
+ auto_fork_validation(parent_number, seal.work.algo, fork_heights, maxi_position),
+ };
+
+ if !valid_algorithm {
+ return Ok(false);
+ }
+
+ log::debug!("✅ Valid algorithm!");
+
+ // See whether the hash meets the difficulty requirement. If not, fail fast.
+ if !multi_hash_meets_difficulty(&seal.work, difficulty) {
+ return Ok(false);
+ }
+
+ log::debug!("✅ Checksum difficulty passed!");
+
+ // Make sure the provided work actually comes from the correct pre_hash
+ let compute = Compute { difficulty, pre_hash: *pre_hash, nonce: seal.nonce };
+
+ if compute.compute(seal.work.algo) != seal {
+ return Ok(false);
+ }
+
+ log::debug!("✅ Re-compute passed!");
+
+ log::debug!("🛠️ All passed, append the block to the chain ...");
+
+ Ok(true)
+ }
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
///
pub struct ForkHeights {
- /// The block height to perform the soft fork that adds sha3 and keccak support.
- pub add_sha3_keccak: u32,
- /// The block height to perform the hard fork that removes md5 support.
- pub remove_md5: u32,
- /// The block height to perform the contentious fork where some become sha3- or keccak-maxis.
- pub split_sha3_keccak: u32,
+ /// The block height to perform the soft fork that adds sha3 and keccak support.
+ pub add_sha3_keccak: u32,
+ /// The block height to perform the hard fork that removes md5 support.
+ pub remove_md5: u32,
+ /// The block height to perform the contentious fork where some become sha3- or keccak-maxis.
+ pub split_sha3_keccak: u32,
}
/// Various political positions a node could take when the network is forking into
/// keccak maxis and sha3 maxis
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum MaxiPosition {
- /// Allow all blocks, both sha3 and keccak
- NoMaxi,
- /// Only allow sha3 blocks
- Sha3Maxi,
- /// Only allow keccak blocks
- KeccakMaxi,
- /// Only allow a single type of blocks. Which type it is is determined by what algo the node is mining.
- FollowMining,
+ /// Allow all blocks, both sha3 and keccak
+ NoMaxi,
+ /// Only allow sha3 blocks
+ Sha3Maxi,
+ /// Only allow keccak blocks
+ KeccakMaxi,
+ /// Only allow a single type of blocks. Which type it is is determined by what algo the node is
+ /// mining.
+ FollowMining,
}
#[derive(Copy, Clone, Eq, PartialEq)]
/// The actual properly typed config after we're done working around all the BS.
pub enum ForkingConfig {
- ///
- Manual,
- ///
- Automatic(ForkHeights, MaxiPosition),
+ ///
+ Manual,
+ ///
+ Automatic(ForkHeights, MaxiPosition),
}
impl FromStr for MaxiPosition {
- type Err = ();
-
- fn from_str(s: &str) -> Result<Self, Self::Err> {
- Ok(match &s.to_lowercase()[..] {
- "allow-all" | "allowall" | "no-maxi" | "nomaxi" => Self::NoMaxi,
- "sha3-maxi" | "sha3maxi" => Self::Sha3Maxi,
- "keccak-maxi" | "keccakmaxi" => Self::KeccakMaxi,
- _ => Self::FollowMining,
- })
- }
+ type Err = ();
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ Ok(match &s.to_lowercase()[..] {
+ "allow-all" | "allowall" | "no-maxi" | "nomaxi" => Self::NoMaxi,
+ "sha3-maxi" | "sha3maxi" => Self::Sha3Maxi,
+ "keccak-maxi" | "keccakmaxi" => Self::KeccakMaxi,
+ _ => Self::FollowMining,
+ })
+ }
}
-/// Manual mode, the node operator manually specifies which hashing algorithms are valid through the mining client.
-/// If you would like to do a fork, simply allow, un-allow some algorithms to check it.
+/// Manual mode, the node operator manually specifies which hashing algorithms are valid through the
+/// mining client. If you would like to do a fork, simply allow, un-allow some algorithms to check
+/// it.
fn manual_fork_validation(_parent_number: u32, algo: SupportedHashes) -> bool {
- use SupportedHashes::*;
-
- // To begin with, allow all algorithms.
- // After the fork height this check is skipped so all the hashes become valid.
- match algo {
- Md5 => true,
- Sha3 => true,
- Keccak => true,
- }
+ use SupportedHashes::*;
+
+ // To begin with, allow all algorithms.
+ // After the fork height this check is skipped so all the hashes become valid.
+ match algo {
+ Md5 => true,
+ Sha3 => true,
+ Keccak => true,
+ }
}
/// In automatic mode, the `ForkHeights` and `MaxiPosition` structs define the forking schedule
/// and the node's behavior during the contentious fork
/// (where the network splits into two chains supporting different hashing algorithms).
/// The validation logic considers the parent block height,
-/// forking configuration parameters, and the hashing algorithm used in the PoW solution to determine its validity.
+/// forking configuration parameters, and the hashing algorithm used in the PoW solution to
+/// determine its validity.
fn auto_fork_validation(
- parent_number: u32,
- algo: SupportedHashes,
- fork_heights: ForkHeights,
- maxi_position: MaxiPosition,
+ parent_number: u32,
+ algo: SupportedHashes,
+ fork_heights: ForkHeights,
+ maxi_position: MaxiPosition,
) -> bool {
- use MaxiPosition::*;
- use SupportedHashes::*;
-
- log::debug!("parent_number: {:?}", parent_number);
- log::debug!("fork_heights: {:?}", fork_heights);
-
- if parent_number < fork_heights.add_sha3_keccak {
- // To begin with we only allow md5 hashes for our pow.
- // After the fork height this check is skipped so all the hashes become valid.
- match algo {
- Md5 => true,
- Sha3 => false,
- Keccak => false,
- }
- } else if parent_number < fork_heights.remove_md5 {
- // After the first fork, all three algos become valid.
- match algo {
- Md5 => true,
- Sha3 => true,
- Keccak => true,
- }
- } else if parent_number < fork_heights.split_sha3_keccak {
- // After the second fork, md5 is no longer valid.
- match algo {
- Md5 => false,
- Sha3 => true,
- Keccak => true,
- }
- } else {
- // Finally we have the contentious fork.
- // Our behavior here depends which maxi position we have taken.
- #[allow(clippy::match_like_matches_macro)]
- match (algo, maxi_position) {
- (Sha3, Sha3Maxi) => true,
- (Sha3, NoMaxi) => true,
- (Keccak, KeccakMaxi) => true,
- (Keccak, NoMaxi) => true,
- _ => false,
- }
- }
+ use MaxiPosition::*;
+ use SupportedHashes::*;
+
+ log::debug!("parent_number: {:?}", parent_number);
+ log::debug!("fork_heights: {:?}", fork_heights);
+
+ if parent_number < fork_heights.add_sha3_keccak {
+ // To begin with we only allow md5 hashes for our pow.
+ // After the fork height this check is skipped so all the hashes become valid.
+ match algo {
+ Md5 => true,
+ Sha3 => false,
+ Keccak => false,
+ }
+ } else if parent_number < fork_heights.remove_md5 {
+ // After the first fork, all three algos become valid.
+ match algo {
+ Md5 => true,
+ Sha3 => true,
+ Keccak => true,
+ }
+ } else if parent_number < fork_heights.split_sha3_keccak {
+ // After the second fork, md5 is no longer valid.
+ match algo {
+ Md5 => false,
+ Sha3 => true,
+ Keccak => true,
+ }
+ } else {
+ // Finally we have the contentious fork.
+ // Our behavior here depends which maxi position we have taken.
+ #[allow(clippy::match_like_matches_macro)]
+ match (algo, maxi_position) {
+ (Sha3, Sha3Maxi) => true,
+ (Sha3, NoMaxi) => true,
+ (Keccak, KeccakMaxi) => true,
+ (Keccak, NoMaxi) => true,
+ _ => false,
+ }
+ }
}
diff --git a/node/build.rs b/node/build.rs
index f97fd98..aa9206c 100644
--- a/node/build.rs
+++ b/node/build.rs
@@ -1,4 +1,4 @@
fn main() {
- substrate_build_script_utils::generate_cargo_keys();
- substrate_build_script_utils::rerun_if_git_head_changed();
+ substrate_build_script_utils::generate_cargo_keys();
+ substrate_build_script_utils::rerun_if_git_head_changed();
}
diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs
index a8e1732..091d0e8 100644
--- a/node/src/chain_spec.rs
+++ b/node/src/chain_spec.rs
@@ -1,19 +1,13 @@
use std::str::FromStr;
use academy_pow_runtime::{
- AccountId,
- SS58Prefix,
- Signature,
- TOKEN_DECIMALS,
- TOKEN_SYMBOL,
- WASM_BINARY,
+ AccountId, SS58Prefix, Signature, TOKEN_DECIMALS, TOKEN_SYMBOL, WASM_BINARY,
};
use multi_pow::{ForkHeights, ForkingConfig, MaxiPosition};
use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup};
use sc_service::ChainType;
use serde::{Deserialize, Serialize};
-use sp_core::{sr25519, Pair, Public};
-use sp_core::{ByteArray, H256};
+use sp_core::{sr25519, ByteArray, Pair, Public, H256};
use sp_runtime::traits::{IdentifyAccount, Verify};
/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type.
@@ -27,58 +21,58 @@ pub type ChainSpec = sc_service::GenericChainSpec<ForkingExtensions>;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)]
#[serde(deny_unknown_fields)]
pub struct ForkingExtensions {
- /// Manual mode is intended for when we you are running a live workshop.
- /// No forking happens automatically. Rather, you have to hard-code the forks.
- ///
- /// If manual mode is enabled, the rest of the parameters are ignored.
- /// This should really be an enum, but I have to work around the broken extension system.
- ///
- /// Aww damn it! I can't even use bool in this broken system? Okay then I guess 0 means automatic mode
- /// and anything else means manual mode.
- pub manual_mode: u32,
- /// The block height to perform the soft fork that adds sha3 and keccak support.
- pub add_sha3_keccak: u32,
- /// The block height to perform the hard fork that removes md5 support.
- pub remove_md5: u32,
- /// The block height to perform the contentious fork where some become sha3- or keccak-maxis.
- pub split_sha3_keccak: u32,
- // Damn extension thing is so fragile, I can't even use an enum here.
- // Let alone that time I tried to use the forked value feature.
- /// The political position that this node will take at the contentious fork.
- pub maxi_position: String,
+ /// Manual mode is intended for when we you are running a live workshop.
+ /// No forking happens automatically. Rather, you have to hard-code the forks.
+ ///
+ /// If manual mode is enabled, the rest of the parameters are ignored.
+ /// This should really be an enum, but I have to work around the broken extension system.
+ ///
+ /// Aww damn it! I can't even use bool in this broken system? Okay then I guess 0 means
+ /// automatic mode and anything else means manual mode.
+ pub manual_mode: u32,
+ /// The block height to perform the soft fork that adds sha3 and keccak support.
+ pub add_sha3_keccak: u32,
+ /// The block height to perform the hard fork that removes md5 support.
+ pub remove_md5: u32,
+ /// The block height to perform the contentious fork where some become sha3- or keccak-maxis.
+ pub split_sha3_keccak: u32,
+ // Damn extension thing is so fragile, I can't even use an enum here.
+ // Let alone that time I tried to use the forked value feature.
+ /// The political position that this node will take at the contentious fork.
+ pub maxi_position: String,
}
impl From<&ForkingExtensions> for ForkingConfig {
- fn from(e: &ForkingExtensions) -> Self {
- if e.manual_mode > 0 {
- return Self::Manual;
- }
-
- let fork_heights = ForkHeights {
- add_sha3_keccak: e.add_sha3_keccak,
- remove_md5: e.remove_md5,
- split_sha3_keccak: e.split_sha3_keccak,
- };
-
- let maxi_position =
- MaxiPosition::from_str(&e.maxi_position).expect("Should have a valid maxi position...");
-
- Self::Automatic(fork_heights, maxi_position)
- }
+ fn from(e: &ForkingExtensions) -> Self {
+ if e.manual_mode > 0 {
+ return Self::Manual;
+ }
+
+ let fork_heights = ForkHeights {
+ add_sha3_keccak: e.add_sha3_keccak,
+ remove_md5: e.remove_md5,
+ split_sha3_keccak: e.split_sha3_keccak,
+ };
+
+ let maxi_position =
+ MaxiPosition::from_str(&e.maxi_position).expect("Should have a valid maxi position...");
+
+ Self::Automatic(fork_heights, maxi_position)
+ }
}
impl ForkingExtensions {
- /// Try to get the extension from the given `ChainSpec`.
- pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> {
- sc_chain_spec::get_extension(chain_spec.extensions())
- }
+ /// Try to get the extension from the given `ChainSpec`.
+ pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> {
+ sc_chain_spec::get_extension(chain_spec.extensions())
+ }
}
/// Generate a crypto pair from seed.
pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
- TPublic::Pair::from_string(&format!("//{}", seed), None)
- .expect("static values are valid; qed")
- .public()
+ TPublic::Pair::from_string(&format!("//{}", seed), None)
+ .expect("static values are valid; qed")
+ .public()
}
type AccountPublic = <Signature as Verify>::Signer;
@@ -86,103 +80,100 @@ type AccountPublic = <Signature as Verify>::Signer;
/// Generate an account ID from seed.
pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId
where
- AccountPublic: From<<TPublic::Pair as Pair>::Public>,
+ AccountPublic: From<<TPublic::Pair as Pair>::Public>,
{
- AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
+ AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
}
pub fn development_config() -> Result<ChainSpec, String> {
- Ok(ChainSpec::builder(
- WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?,
- ForkingExtensions {
- manual_mode: 1, // change this to `0` if you want to try `auto_fork_validation`
- add_sha3_keccak: 10,
- remove_md5: 20,
- split_sha3_keccak: 30,
- maxi_position: String::from("follow-mining"),
- },
- )
- .with_name("Development")
- .with_id("dev")
- .with_chain_type(ChainType::Development)
- .with_genesis_config_patch(genesis(
- // Pre-funded accounts
- vec![
- get_account_id_from_seed::<sr25519::Public>("Alice"),
- get_account_id_from_seed::<sr25519::Public>("Bob"),
- get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
- get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
- ],
- // Initial Difficulty
- 4_000_000,
- ))
- .with_properties(system_properties())
- .build())
+ Ok(ChainSpec::builder(
+ WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?,
+ ForkingExtensions {
+ manual_mode: 1, // change this to `0` if you want to try `auto_fork_validation`
+ add_sha3_keccak: 10,
+ remove_md5: 20,
+ split_sha3_keccak: 30,
+ maxi_position: String::from("follow-mining"),
+ },
+ )
+ .with_name("Development")
+ .with_id("dev")
+ .with_chain_type(ChainType::Development)
+ .with_genesis_config_patch(genesis(
+ // Pre-funded accounts
+ vec![
+ get_account_id_from_seed::<sr25519::Public>("Alice"),
+ get_account_id_from_seed::<sr25519::Public>("Bob"),
+ get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
+ get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
+ ],
+ // Initial Difficulty
+ 4_000_000,
+ ))
+ .with_properties(system_properties())
+ .build())
}
pub fn testnet_config() -> Result<ChainSpec, String> {
- Ok(ChainSpec::builder(
- WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?,
- ForkingExtensions {
- manual_mode: 1,
- add_sha3_keccak: 0,
- remove_md5: 0,
- split_sha3_keccak: 0,
- maxi_position: String::new(),
- },
- )
- .with_name("Testnet")
- .with_id("testnet")
- .with_chain_type(ChainType::Local)
- .with_genesis_config_patch(genesis(
- vec![
- get_account_id_from_seed::<sr25519::Public>("Alice"),
- get_account_id_from_seed::<sr25519::Public>("Bob"),
- get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
- get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
- ],
- 4_000_000,
- ))
- .with_properties(system_properties())
- .build())
+ Ok(ChainSpec::builder(
+ WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?,
+ ForkingExtensions {
+ manual_mode: 1,
+ add_sha3_keccak: 0,
+ remove_md5: 0,
+ split_sha3_keccak: 0,
+ maxi_position: String::new(),
+ },
+ )
+ .with_name("Testnet")
+ .with_id("testnet")
+ .with_chain_type(ChainType::Local)
+ .with_genesis_config_patch(genesis(
+ vec![
+ get_account_id_from_seed::<sr25519::Public>("Alice"),
+ get_account_id_from_seed::<sr25519::Public>("Bob"),
+ get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
+ get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
+ ],
+ 4_000_000,
+ ))
+ .with_properties(system_properties())
+ .build())
}
-fn genesis(
- endowed_accounts: Vec<AccountId>,
- initial_difficulty: u32,
-) -> serde_json::Value {
- serde_json::json!({
- "balances": {
- // Configure endowed accounts with initial balance of 1 << 50.
- "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 50)).collect::<Vec<_>>(),
- },
- "keccakDifficultyAdjustment": {
- "initialDifficulty": u32_to_u8_32(initial_difficulty),
- },
- "md5DifficultyAdjustment": {
- "initialDifficulty": u32_to_u8_32(initial_difficulty),
- },
- "sha3DifficultyAdjustment": {
- "initialDifficulty": u32_to_u8_32(initial_difficulty),
- },
- })
+fn genesis(endowed_accounts: Vec<AccountId>, initial_difficulty: u32) -> serde_json::Value {
+ serde_json::json!({
+ "balances": {
+ // Configure endowed accounts with initial balance of 1 << 50.
+ "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 50)).collect::<Vec<_>>(),
+ },
+ "keccakDifficultyAdjustment": {
+ "initialDifficulty": u32_to_u8_32(initial_difficulty),
+ },
+ "md5DifficultyAdjustment": {
+ "initialDifficulty": u32_to_u8_32(initial_difficulty),
+ },
+ "sha3DifficultyAdjustment": {
+ "initialDifficulty": u32_to_u8_32(initial_difficulty),
+ },
+ })
}
/// Convert u32 (default value) to [u8;32] (U256)
/// in little-endian format
pub fn u32_to_u8_32(num: u32) -> [u8; 32] {
- let mut result = [0u8; 32];
- let bytes = num.to_le_bytes();
- result[..4].copy_from_slice(&bytes);
- result
+ let mut result = [0u8; 32];
+ let bytes = num.to_le_bytes();
+ result[..4].copy_from_slice(&bytes);
+ result
}
fn system_properties() -> sc_chain_spec::Properties {
- let mut properties = sc_chain_spec::Properties::new();
+ let mut properties = sc_chain_spec::Properties::new();
- properties.insert("ss58Format".into(), SS58Prefix::get().into());
- properties.insert("tokenSymbol".into(), TOKEN_SYMBOL.into());
- properties.insert("tokenDecimals".into(), TOKEN_DECIMALS.into());
+ properties.insert("ss58Format".into(), SS58Prefix::get().into());
+ properties.insert("tokenSymbol".into(), TOKEN_SYMBOL.into());
+ properties.insert("tokenDecimals".into(), TOKEN_DECIMALS.into());
- properties
+ properties
}
diff --git a/node/src/cli.rs b/node/src/cli.rs
index 4e1ac61..e7a62e5 100644
--- a/node/src/cli.rs
+++ b/node/src/cli.rs
@@ -1,8 +1,8 @@
use academy_pow_runtime::AccountId;
use multi_pow::SupportedHashes;
use sc_cli::{
- clap::{ArgGroup, Parser},
- RunCmd,
+ clap::{ArgGroup, Parser},
+ RunCmd,
};
use sc_service::ChainType;
use sp_core::{crypto::Ss58Codec, sr25519};
@@ -10,139 +10,136 @@ use sp_core::{crypto::Ss58Codec, sr25519};
#[derive(Debug, Parser)]
#[clap(subcommand_negates_reqs(true), version(env!("SUBSTRATE_CLI_IMPL_VERSION")))]
pub struct Cli {
- #[clap(subcommand)]
- pub subcommand: Option<Subcommand>,
+ #[clap(subcommand)]
+ pub subcommand: Option<Subcommand>,
- #[command(flatten)]
- pub pow: AcademyPowCli,
+ #[command(flatten)]
+ pub pow: AcademyPowCli,
- #[clap(flatten)]
- pub run: RunCmd,
+ #[clap(flatten)]
+ pub run: RunCmd,
}
#[derive(Debug, Parser, Clone)]
#[clap(group(ArgGroup::new("backup")))]
pub struct AcademyPowCli {
- /// Miner's AccountId (base58 encoding of an SR25519 public key) for the block rewards
- #[clap(long,
+ /// Miner's AccountId (base58 encoding of an SR25519 public key) for the block rewards
+ #[clap(long,
conflicts_with = "mining_public_key",
value_parser = parse_account_id)]
- pub mining_account_id: Option<AccountId>,
+ pub mining_account_id: Option<AccountId>,
- /// Miner's hex encoding of the SR25519 public key) for the block rewards
- #[clap(
+ /// Miner's hex encoding of the SR25519 public key) for the block rewards
+ #[clap(
long,
conflicts_with = "mining_account_id",
value_parser = parse_sr25519_public_key
)]
- pub mining_public_key: Option<sr25519::Public>,
+ pub mining_public_key: Option<sr25519::Public>,
- /// The mining algorithm to use
- #[clap(long, value_parser = parse_algo, default_value = "md5")]
- pub mining_algo: multi_pow::SupportedHashes,
+ /// The mining algorithm to use
+ #[clap(long, value_parser = parse_algo, default_value = "md5")]
+ pub mining_algo: multi_pow::SupportedHashes,
- /// whether to use instant seal
- #[clap(long, default_value = "false")]
- pub instant_seal: bool,
+ /// whether to use instant seal
+ #[clap(long, default_value = "false")]
+ pub instant_seal: bool,
}
impl AcademyPowCli {
- pub fn public_key_bytes(&self, keyring: Option<sp_keyring::Sr25519Keyring>) -> [u8; 32] {
- match &self.mining_account_id {
- Some(account_id) => *account_id.as_ref(),
- None => match self.mining_public_key {
- Some(public_key) => public_key.0,
- None => keyring.map(|k| k.to_raw_public()).unwrap_or([0u8; 32]),
- },
- }
- }
+ pub fn public_key_bytes(&self, keyring: Option<sp_keyring::Sr25519Keyring>) -> [u8; 32] {
+ match &self.mining_account_id {
+ Some(account_id) => *account_id.as_ref(),
+ None => match self.mining_public_key {
+ Some(public_key) => public_key.0,
+ None => keyring.map(|k| k.to_raw_public()).unwrap_or([0u8; 32]),
+ },
+ }
+ }
}
#[derive(Debug, Parser)]
pub struct BuildSpecCmd {
- #[clap(flatten)]
- pub base: sc_cli::BuildSpecCmd,
+ #[clap(flatten)]
+ pub base: sc_cli::BuildSpecCmd,
- /// Chain name.
- #[arg(long, default_value = "Academy PoW")]
- pub chain_name: String,
+ /// Chain name.
+ #[arg(long, default_value = "Academy PoW")]
+ pub chain_name: String,
- /// Chain ID is a short identifier of the chain
- #[arg(long, value_name = "ID", default_value = "academy_pow")]
- pub chain_id: String,
+ /// Chain ID is a short identifier of the chain
+ #[arg(long, value_name = "ID", default_value = "academy_pow")]
+ pub chain_id: String,
- /// AccountIds of the optional rich accounts
- #[arg(long, value_delimiter = ',', value_parser = parse_account_id, num_args=1..)]
- pub endowed_accounts: Option<Vec<AccountId>>,
+ /// AccountIds of the optional rich accounts
+ #[arg(long, value_delimiter = ',', value_parser = parse_account_id, num_args=1..)]
+ pub endowed_accounts: Option<Vec<AccountId>>,
- /// The type of the chain. Possible values: "dev", "local", "live" (default)
- #[arg(long, value_name = "TYPE", value_parser = parse_chaintype, default_value = "live")]
- pub chain_type: ChainType,
+ /// The type of the chain. Possible values: "dev", "local", "live" (default)
+ #[arg(long, value_name = "TYPE", value_parser = parse_chaintype, default_value = "live")]
+ pub chain_type: ChainType,
- #[arg(long, default_value = "4000000")]
- pub initial_difficulty: u32,
+ #[arg(long, default_value = "4000000")]
+ pub initial_difficulty: u32,
}
fn parse_algo(s: &str) -> Result<SupportedHashes, String> {
- Ok(match s {
- "md" | "Md" | "md5" | "Md5" => SupportedHashes::Md5,
- "sha" | "sha3" | "Sha" | "Sha3" => SupportedHashes::Sha3,
- "keccak" | "Keccak" => SupportedHashes::Keccak,
- s => panic!(
- "Wrong mining algo: {}. Possible values: md5, sha3, keccak",
- s
- ),
- })
+ Ok(match s {
+ "md" | "Md" | "md5" | "Md5" => SupportedHashes::Md5,
+ "sha" | "sha3" | "Sha" | "Sha3" => SupportedHashes::Sha3,
+ "keccak" | "Keccak" => SupportedHashes::Keccak,
+ s => panic!("Wrong mining algo: {}. Possible values: md5, sha3, keccak", s),
+ })
}
fn parse_chaintype(s: &str) -> Result<ChainType, String> {
- Ok(match s {
- "dev" => ChainType::Development,
- "local" => ChainType::Local,
- "live" => ChainType::Live,
- s => panic!("Wrong chain type {} Possible values: dev local live", s),
- })
+ Ok(match s {
+ "dev" => ChainType::Development,
+ "local" => ChainType::Local,
+ "live" => ChainType::Live,
+ s => panic!("Wrong chain type {} Possible values: dev local live", s),
+ })
}
/// Parse AccountId from a string argument passed on the command line.
fn parse_account_id(s: &str) -> Result<AccountId, String> {
- Ok(AccountId::from_string(s)
- .expect("Passed string is not a bas58 encoding of a sr25519 public key"))
+ Ok(AccountId::from_string(s)
+ .expect("Passed string is not a bas58 encoding of a sr25519 public key"))
}
/// Parse sr25519 pubkey from a string argument passed on the command line.
fn parse_sr25519_public_key(s: &str) -> Result<sr25519::Public, String> {
- Ok(sr25519::Public::from_string(s)
- .expect("Passed string is not a hex encoding of a sr25519 public key"))
+ Ok(sr25519::Public::from_string(s)
+ .expect("Passed string is not a hex encoding of a sr25519 public key"))
}
#[derive(Debug, clap::Subcommand)]
pub enum Subcommand {
- /// Key management cli utilities
- #[command(subcommand)]
- Key(sc_cli::KeySubcommand),
+ /// Key management cli utilities
+ #[command(subcommand)]
+ Key(sc_cli::KeySubcommand),
- /// Build a chain specification.
- BuildSpec(BuildSpecCmd),
+ /// Build a chain specification.
+ BuildSpec(BuildSpecCmd),
- /// Validate blocks.
- CheckBlock(sc_cli::CheckBlockCmd),
+ /// Validate blocks.
+ CheckBlock(sc_cli::CheckBlockCmd),
- /// Export blocks.
- ExportBlocks(sc_cli::ExportBlocksCmd),
+ /// Export blocks.
+ ExportBlocks(sc_cli::ExportBlocksCmd),
- /// Export the state of a given block into a chain spec.
- ExportState(sc_cli::ExportStateCmd),
+ /// Export the state of a given block into a chain spec.
+ ExportState(sc_cli::ExportStateCmd),
- /// Import blocks.
- ImportBlocks(sc_cli::ImportBlocksCmd),
+ /// Import blocks.
+ ImportBlocks(sc_cli::ImportBlocksCmd),
- /// Remove the whole chain.
- PurgeChain(sc_cli::PurgeChainCmd),
+ /// Remove the whole chain.
+ PurgeChain(sc_cli::PurgeChainCmd),
- /// Revert the chain to a previous state.
- Revert(sc_cli::RevertCmd),
+ /// Revert the chain to a previous state.
+ Revert(sc_cli::RevertCmd),
- /// Db meta columns information.
- ChainInfo(sc_cli::ChainInfoCmd),
+ /// Db meta columns information.
+ ChainInfo(sc_cli::ChainInfoCmd),
}
diff --git a/node/src/command.rs b/node/src/command.rs
index b62bd98..18c5c54 100644
--- a/node/src/command.rs
+++ b/node/src/command.rs
@@ -21,163 +21,146 @@ use sc_service::PartialComponents;
use sp_core::sr25519;
use crate::{
- chain_spec::{self, ForkingExtensions},
- cli::{Cli, Subcommand},
- service,
+ chain_spec::{self, ForkingExtensions},
+ cli::{Cli, Subcommand},
+ service,
};
impl SubstrateCli for Cli {
- fn impl_name() -> String {
- "Academy PoW Chain".into()
- }
-
- fn impl_version() -> String {
- env!("SUBSTRATE_CLI_IMPL_VERSION").into()
- }
-
- fn executable_name() -> String {
- env!("CARGO_PKG_NAME").into()
- }
-
- fn author() -> String {
- env!("CARGO_PKG_AUTHORS").into()
- }
-
- fn description() -> String {
- env!("CARGO_PKG_DESCRIPTION").into()
- }
-
- fn support_url() -> String {
- "https://github.com/danielbui12/substrate-bitcoin-like-blockchain/issues/new".into()
- }
-
- fn copyright_start_year() -> i32 {
- 2025
- }
-
- fn load_spec(&self, id: &str) -> Result<Box<dyn sc_service::ChainSpec>, String> {
- Ok(match id {
- "" => Box::new(chain_spec::ChainSpec::from_json_bytes(
- &include_bytes!("../../spec.json")[..],
- )?),
- "dev" => Box::new(chain_spec::development_config()?),
- "local" => Box::new(chain_spec::testnet_config()?),
- path => Box::new(chain_spec::ChainSpec::from_json_file(
- std::path::PathBuf::from(path),
- )?),
- })
- }
+ fn impl_name() -> String {
+ "Academy PoW Chain".into()
+ }
+
+ fn impl_version() -> String {
+ env!("SUBSTRATE_CLI_IMPL_VERSION").into()
+ }
+
+ fn executable_name() -> String {
+ env!("CARGO_PKG_NAME").into()
+ }
+
+ fn author() -> String {
+ env!("CARGO_PKG_AUTHORS").into()
+ }
+
+ fn description() -> String {
+ env!("CARGO_PKG_DESCRIPTION").into()
+ }
+
+ fn support_url() -> String {
+ "https://github.com/danielbui12/substrate-bitcoin-like-blockchain/issues/new".into()
+ }
+
+ fn copyright_start_year() -> i32 {
+ 2025
+ }
+
+ fn load_spec(&self, id: &str) -> Result<Box<dyn sc_service::ChainSpec>, String> {
+ Ok(match id {
+ "" => Box::new(chain_spec::ChainSpec::from_json_bytes(
+ &include_bytes!("../../spec.json")[..],
+ )?),
+ "dev" => Box::new(chain_spec::development_config()?),
+ "local" => Box::new(chain_spec::testnet_config()?),
+ path =>
+ Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?),
+ })
+ }
}
/// Parse and run command line arguments
pub fn run() -> sc_cli::Result<()> {
- let cli = Cli::from_args();
-
- match &cli.subcommand {
- Some(Subcommand::Key(cmd)) => cmd.run(&cli),
- Some(Subcommand::BuildSpec(cmd)) => {
- let runner = cli.create_runner(&cmd.base)?;
- runner.sync_run(|config| cmd.base.run(config.chain_spec, config.network))
- }
- Some(Subcommand::CheckBlock(cmd)) => {
- let runner = cli.create_runner(cmd)?;
- runner.async_run(|config| {
- let PartialComponents {
- client,
- task_manager,
- import_queue,
- ..
- } = service::new_partial(&config, ForkingConfig::Manual)?;
- Ok((cmd.run(client, import_queue), task_manager))
- })
- }
- Some(Subcommand::ExportBlocks(cmd)) => {
- let runner = cli.create_runner(cmd)?;
- runner.async_run(|config| {
- let PartialComponents {
- client,
- task_manager,
- ..
- } = service::new_partial(&config, ForkingConfig::Manual)?;
- Ok((cmd.run(client, config.database), task_manager))
- })
- }
- Some(Subcommand::ExportState(cmd)) => {
- let runner = cli.create_runner(cmd)?;
- runner.async_run(|config| {
- let PartialComponents {
- client,
- task_manager,
- ..
- } = service::new_partial(&config, ForkingConfig::Manual)?;
- Ok((cmd.run(client, config.chain_spec), task_manager))
- })
- }
- Some(Subcommand::ImportBlocks(cmd)) => {
- let runner = cli.create_runner(cmd)?;
- runner.async_run(|config| {
- let PartialComponents {
- client,
- task_manager,
- import_queue,
- ..
- } = service::new_partial(&config, ForkingConfig::Manual)?;
- Ok((cmd.run(client, import_queue), task_manager))
- })
- }
- Some(Subcommand::PurgeChain(cmd)) => {
- let runner = cli.create_runner(cmd)?;
- runner.sync_run(|config| cmd.run(config.database))
- }
- Some(Subcommand::Revert(cmd)) => {
- let runner = cli.create_runner(cmd)?;
- runner.async_run(|config| {
- let PartialComponents {
- client,
- task_manager,
- backend,
- ..
- } = service::new_partial(&config, ForkingConfig::Manual)?;
- Ok((cmd.run(client, backend, None), task_manager))
- })
- }
- Some(Subcommand::ChainInfo(cmd)) => {
- let runner = cli.create_runner(cmd)?;
- runner.sync_run(|config| cmd.run::<Block>(&config))
- }
- None => {
- // Get the mining account from the cli
- let bytes: [u8; 32] = cli.pow.public_key_bytes(cli.run.get_keyring());
- let sr25519_public_key = sr25519::Public::from_raw(bytes);
-
- let runner = cli.create_runner(&cli.run)?;
- runner.run_node_until_exit(|config| async move {
- // Get the forking information from the chain spec extension.
- // Convert it to a strong type, and fill in the proper maxi position if they are following mining.
- let forking_extension = ForkingExtensions::try_get(&*config.chain_spec)
- .expect("Should be able to get the fork config from the extension");
- let forking_config = match ForkingConfig::from(forking_extension) {
- ForkingConfig::Automatic(fork_heights, MaxiPosition::FollowMining) => {
- let maxi_position = match cli.pow.mining_algo {
- multi_pow::SupportedHashes::Md5 => MaxiPosition::NoMaxi,
- multi_pow::SupportedHashes::Sha3 => MaxiPosition::Sha3Maxi,
- multi_pow::SupportedHashes::Keccak => MaxiPosition::KeccakMaxi,
- };
- ForkingConfig::Automatic(fork_heights, maxi_position)
- }
- old_config => old_config,
- };
-
- service::new_full::<sc_network::Litep2pNetworkBackend>(
- config,
- forking_config,
- //TODO Combine the following three fields into a MiningConfig analogous to the ForkingConfig
- sr25519_public_key,
- cli.pow.instant_seal,
- cli.pow.mining_algo,
- )
- .map_err(sc_cli::Error::Service)
- })
- }
- }
+ let cli = Cli::from_args();
+
+ match &cli.subcommand {
+ Some(Subcommand::Key(cmd)) => cmd.run(&cli),
+ Some(Subcommand::BuildSpec(cmd)) => {
+ let runner = cli.create_runner(&cmd.base)?;
+ runner.sync_run(|config| cmd.base.run(config.chain_spec, config.network))
+ },
+ Some(Subcommand::CheckBlock(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+ runner.async_run(|config| {
+ let PartialComponents { client, task_manager, import_queue, .. } =
+ service::new_partial(&config, ForkingConfig::Manual)?;
+ Ok((cmd.run(client, import_queue), task_manager))
+ })
+ },
+ Some(Subcommand::ExportBlocks(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+ runner.async_run(|config| {
+ let PartialComponents { client, task_manager, .. } =
+ service::new_partial(&config, ForkingConfig::Manual)?;
+ Ok((cmd.run(client, config.database), task_manager))
+ })
+ },
+ Some(Subcommand::ExportState(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+ runner.async_run(|config| {
+ let PartialComponents { client, task_manager, .. } =
+ service::new_partial(&config, ForkingConfig::Manual)?;
+ Ok((cmd.run(client, config.chain_spec), task_manager))
+ })
+ },
+ Some(Subcommand::ImportBlocks(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+ runner.async_run(|config| {
+ let PartialComponents { client, task_manager, import_queue, .. } =
+ service::new_partial(&config, ForkingConfig::Manual)?;
+ Ok((cmd.run(client, import_queue), task_manager))
+ })
+ },
+ Some(Subcommand::PurgeChain(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+ runner.sync_run(|config| cmd.run(config.database))
+ },
+ Some(Subcommand::Revert(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+ runner.async_run(|config| {
+ let PartialComponents { client, task_manager, backend, .. } =
+ service::new_partial(&config, ForkingConfig::Manual)?;
+ Ok((cmd.run(client, backend, None), task_manager))
+ })
+ },
+ Some(Subcommand::ChainInfo(cmd)) => {
+ let runner = cli.create_runner(cmd)?;
+ runner.sync_run(|config| cmd.run::<Block>(&config))
+ },
+ None => {
+ // Get the mining account from the cli
+ let bytes: [u8; 32] = cli.pow.public_key_bytes(cli.run.get_keyring());
+ let sr25519_public_key = sr25519::Public::from_raw(bytes);
+
+ let runner = cli.create_runner(&cli.run)?;
+ runner.run_node_until_exit(|config| async move {
+ // Get the forking information from the chain spec extension.
+ // Convert it to a strong type, and fill in the proper maxi position if they are
+ // following mining.
+ let forking_extension = ForkingExtensions::try_get(&*config.chain_spec)
+ .expect("Should be able to get the fork config from the extension");
+ let forking_config = match ForkingConfig::from(forking_extension) {
+ ForkingConfig::Automatic(fork_heights, MaxiPosition::FollowMining) => {
+ let maxi_position = match cli.pow.mining_algo {
+ multi_pow::SupportedHashes::Md5 => MaxiPosition::NoMaxi,
+ multi_pow::SupportedHashes::Sha3 => MaxiPosition::Sha3Maxi,
+ multi_pow::SupportedHashes::Keccak => MaxiPosition::KeccakMaxi,
+ };
+ ForkingConfig::Automatic(fork_heights, maxi_position)
+ },
+ old_config => old_config,
+ };
+
+ service::new_full::<sc_network::Litep2pNetworkBackend>(
+ config,
+ forking_config,
+ //TODO Combine the following three fields into a MiningConfig analogous to the
+ // ForkingConfig
+ sr25519_public_key,
+ cli.pow.instant_seal,
+ cli.pow.mining_algo,
+ )
+ .map_err(sc_cli::Error::Service)
+ })
+ },
+ }
}
diff --git a/node/src/main.rs b/node/src/main.rs
index a4182cd..4449d28 100644
--- a/node/src/main.rs
+++ b/node/src/main.rs
@@ -9,5 +9,5 @@ mod command;
mod rpc;
fn main() -> sc_cli::Result<()> {
- command::run()
+ command::run()
}
diff --git a/node/src/rpc.rs b/node/src/rpc.rs
index 875414c..c315565 100644
--- a/node/src/rpc.rs
+++ b/node/src/rpc.rs
@@ -17,39 +17,35 @@ use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata};
/// Full client dependencies.
pub struct FullDeps<C, P> {
- /// The client instance to use.
- pub client: Arc<C>,
- /// Transaction pool instance.
- pub pool: Arc<P>,
- /// Whether to deny unsafe calls
- pub deny_unsafe: DenyUnsafe,
+ /// The client instance to use.
+ pub client: Arc<C>,
+ /// Transaction pool instance.
+ pub pool: Arc<P>,
+ /// Whether to deny unsafe calls
+ pub deny_unsafe: DenyUnsafe,
}
/// Instantiate all full RPC extensions.
pub fn create_full<C, P>(
- deps: FullDeps<C, P>,
+ deps: FullDeps<C, P>,
) -> Result<RpcModule<()>, Box<dyn std::error::Error + Send + Sync>>
where
- C: ProvideRuntimeApi<Block>,
- C: HeaderBackend<Block> + HeaderMetadata<Block, Error = BlockChainError> + 'static,
- C: Send + Sync + 'static,
- C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>,
- C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>,
- C::Api: BlockBuilder<Block>,
- P: TransactionPool + 'static,
+ C: ProvideRuntimeApi<Block>,
+ C: HeaderBackend<Block> + HeaderMetadata<Block, Error = BlockChainError> + 'static,
+ C: Send + Sync + 'static,
+ C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>,
+ C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>,
+ C::Api: BlockBuilder<Block>,
+ P: TransactionPool + 'static,
{
- use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer};
- use substrate_frame_rpc_system::{System, SystemApiServer};
+ use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer};
+ use substrate_frame_rpc_system::{System, SystemApiServer};
- let mut module = RpcModule::new(());
- let FullDeps {
- client,
- pool,
- deny_unsafe,
- } = deps;
+ let mut module = RpcModule::new(());
+ let FullDeps { client, pool, deny_unsafe } = deps;
- module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?;
- module.merge(TransactionPayment::new(client).into_rpc())?;
+ module.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?;
+ module.merge(TransactionPayment::new(client).into_rpc())?;
- Ok(module)
+ Ok(module)
}
diff --git a/node/src/service.rs b/node/src/service.rs
index 4bf3935..abf64b5 100644
--- a/node/src/service.rs
+++ b/node/src/service.rs
@@ -23,266 +23,261 @@ type BasicImportQueue = sc_consensus::DefaultImportQueue<Block>;
type BoxBlockImport = sc_consensus::BoxBlockImport<Block>;
pub type Service = PartialComponents<
- FullClient,
- FullBackend,
- FullSelectChain,
- BasicImportQueue,
- sc_transaction_pool::FullPool<Block, FullClient>,
- (BoxBlockImport, Option<Telemetry>),
+ FullClient,
+ FullBackend,
+ FullSelectChain,
+ BasicImportQueue,
+ sc_transaction_pool::FullPool<Block, FullClient>,
+ (BoxBlockImport, Option<Telemetry>),
>;
/// Returns most parts of a service. Not enough to run a full chain,
/// But enough to perform chain operations like purge-chain
#[allow(clippy::type_complexity)]
pub fn new_partial(
- config: &Configuration,
- fork_config: ForkingConfig,
+ config: &Configuration,
+ fork_config: ForkingConfig,
) -> Result<Service, ServiceError> {
- let telemetry = config
- .telemetry_endpoints
- .clone()
- .filter(|x| !x.is_empty())
- .map(|endpoints| -> Result<_, sc_telemetry::Error> {
- let worker = TelemetryWorker::new(16)?;
- let telemetry = worker.handle().new_telemetry(endpoints);
- Ok((worker, telemetry))
- })
- .transpose()?;
+ let telemetry = config
+ .telemetry_endpoints
+ .clone()
+ .filter(|x| !x.is_empty())
+ .map(|endpoints| -> Result<_, sc_telemetry::Error> {
+ let worker = TelemetryWorker::new(16)?;
+ let telemetry = worker.handle().new_telemetry(endpoints);
+ Ok((worker, telemetry))
+ })
+ .transpose()?;
- let executor = sc_service::new_wasm_executor::<sp_io::SubstrateHostFunctions>(config);
- let (client, backend, keystore_container, task_manager) =
- sc_service::new_full_parts::<Block, RuntimeApi, _>(
- config,
- telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
- executor,
- )?;
- let client = Arc::new(client);
+ let executor = sc_service::new_wasm_executor::<sp_io::SubstrateHostFunctions>(config);
+ let (client, backend, keystore_container, task_manager) =
+ sc_service::new_full_parts::<Block, RuntimeApi, _>(
+ config,
+ telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()),
+ executor,
+ )?;
+ let client = Arc::new(client);
- let telemetry = telemetry.map(|(worker, telemetry)| {
- task_manager
- .spawn_handle()
- .spawn("telemetry", None, worker.run());
- telemetry
- });
+ let telemetry = telemetry.map(|(worker, telemetry)| {
+ task_manager.spawn_handle().spawn("telemetry", None, worker.run());
+ telemetry
+ });
- let select_chain = LongestChain::new(backend.clone());
+ let select_chain = LongestChain::new(backend.clone());
- let transaction_pool = sc_transaction_pool::BasicPool::new_full(
- config.transaction_pool.clone(),
- config.role.is_authority().into(),
- config.prometheus_registry(),
- task_manager.spawn_essential_handle(),
- client.clone(),
- );
+ let transaction_pool = sc_transaction_pool::BasicPool::new_full(
+ config.transaction_pool.clone(),
+ config.role.is_authority().into(),
+ config.prometheus_registry(),
+ task_manager.spawn_essential_handle(),
+ client.clone(),
+ );
- let block_import = sc_consensus_pow::PowBlockImport::new(
- client.clone(),
- client.clone(),
- MultiPow::new(client.clone(), fork_config),
- 0, // check inherents starting at block 0
- select_chain.clone(),
- move |_, ()| async move {
- let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
+ let block_import = sc_consensus_pow::PowBlockImport::new(
+ client.clone(),
+ client.clone(),
+ MultiPow::new(client.clone(), fork_config),
+ 0, // check inherents starting at block 0
+ select_chain.clone(),
+ move |_, ()| async move {
+ let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
- // We don't need the current mining key to check inherents, so we just use a default.
- // TODO, I don't think we need to do any checking here at all, right?
- // So can I just remove the author entirely?
- let author =
- academy_pow_runtime::block_author::InherentDataProvider(Default::default());
+ // We don't need the current mining key to check inherents, so we just use a default.
+ // TODO, I don't think we need to do any checking here at all, right?
+ // So can I just remove the author entirely?
+ let author =
+ academy_pow_runtime::block_author::InherentDataProvider(Default::default());
- Ok((timestamp, author))
- },
- );
+ Ok((timestamp, author))
+ },
+ );
- let import_queue = sc_consensus_pow::import_queue(
- Box::new(block_import.clone()),
- None,
- MultiPow::new(client.clone(), fork_config),
- &task_manager.spawn_essential_handle(),
- config.prometheus_registry(),
- )?;
+ let import_queue = sc_consensus_pow::import_queue(
+ Box::new(block_import.clone()),
+ None,
+ MultiPow::new(client.clone(), fork_config),
+ &task_manager.spawn_essential_handle(),
+ config.prometheus_registry(),
+ )?;
- Ok(PartialComponents {
- client,
- backend,
- task_manager,
- import_queue,
- keystore_container,
- select_chain,
- transaction_pool,
- other: (Box::new(block_import), telemetry),
- })
+ Ok(PartialComponents {
+ client,
+ backend,
+ task_manager,
+ import_queue,
+ keystore_container,
+ select_chain,
+ transaction_pool,
+ other: (Box::new(block_import), telemetry),
+ })
}
/// Builds a new service for a full client.
pub fn new_full<
- N: sc_network::NetworkBackend<Block, <Block as sp_runtime::traits::Block>::Hash>,
+ N: sc_network::NetworkBackend<Block, <Block as sp_runtime::traits::Block>::Hash>,
>(
- config: Configuration,
- fork_config: ForkingConfig,
- sr25519_public_key: sr25519::Public,
- instant_seal: bool,
- mining_algo: SupportedHashes,
+ config: Configuration,
+ fork_config: ForkingConfig,
+ sr25519_public_key: sr25519::Public,
+ instant_seal: bool,
+ mining_algo: SupportedHashes,
) -> Result<TaskManager, ServiceError> {
- let sc_service::PartialComponents {
- client,
- backend,
- mut task_manager,
- import_queue,
- keystore_container,
- select_chain,
- transaction_pool,
- other: (pow_block_import, mut telemetry),
- } = new_partial(&config, fork_config)?;
+ let sc_service::PartialComponents {
+ client,
+ backend,
+ mut task_manager,
+ import_queue,
+ keystore_container,
+ select_chain,
+ transaction_pool,
+ other: (pow_block_import, mut telemetry),
+ } = new_partial(&config, fork_config)?;
- let net_config = sc_network::config::FullNetworkConfiguration::<
- Block,
- <Block as sp_runtime::traits::Block>::Hash,
- N,
- >::new(&config.network);
+ let net_config = sc_network::config::FullNetworkConfiguration::<
+ Block,
+ <Block as sp_runtime::traits::Block>::Hash,
+ N,
+ >::new(&config.network);
let metrics = sc_network::NotificationMetrics::new(None);
- let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) =
- sc_service::build_network(sc_service::BuildNetworkParams {
- config: &config,
- net_config,
- client: client.clone(),
- transaction_pool: transaction_pool.clone(),
- spawn_handle: task_manager.spawn_handle(),
- import_queue,
- block_announce_validator_builder: None,
- warp_sync_params: None,
- block_relay: None,
- metrics: metrics,
- })?;
+ let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) =
+ sc_service::build_network(sc_service::BuildNetworkParams {
+ config: &config,
+ net_config,
+ client: client.clone(),
+ transaction_pool: transaction_pool.clone(),
+ spawn_handle: task_manager.spawn_handle(),
+ import_queue,
+ block_announce_validator_builder: None,
+ warp_sync_params: None,
+ block_relay: None,
+ metrics,
+ })?;
- let role = config.role.clone();
- let prometheus_registry = config.prometheus_registry().cloned();
+ let role = config.role.clone();
+ let prometheus_registry = config.prometheus_registry().cloned();
- let rpc_extensions_builder = {
- let client = client.clone();
- let pool = transaction_pool.clone();
+ let rpc_extensions_builder = {
+ let client = client.clone();
+ let pool = transaction_pool.clone();
- Box::new(move |deny_unsafe, _| {
- let deps = crate::rpc::FullDeps {
- client: client.clone(),
- pool: pool.clone(),
- deny_unsafe,
- };
- crate::rpc::create_full(deps).map_err(Into::into)
- })
- };
+ Box::new(move |deny_unsafe, _| {
+ let deps =
+ crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), deny_unsafe };
+ crate::rpc::create_full(deps).map_err(Into::into)
+ })
+ };
- sc_service::spawn_tasks(sc_service::SpawnTasksParams {
- network,
- client: client.clone(),
- keystore: keystore_container.keystore(),
- task_manager: &mut task_manager,
- transaction_pool: transaction_pool.clone(),
- rpc_builder: rpc_extensions_builder,
- backend,
- system_rpc_tx,
- tx_handler_controller,
- sync_service: sync_service.clone(),
- config,
- telemetry: telemetry.as_mut(),
- })?;
+ sc_service::spawn_tasks(sc_service::SpawnTasksParams {
+ network,
+ client: client.clone(),
+ keystore: keystore_container.keystore(),
+ task_manager: &mut task_manager,
+ transaction_pool: transaction_pool.clone(),
+ rpc_builder: rpc_extensions_builder,
+ backend,
+ system_rpc_tx,
+ tx_handler_controller,
+ sync_service: sync_service.clone(),
+ config,
+ telemetry: telemetry.as_mut(),
+ })?;
- if role.is_authority() {
- let proposer = sc_basic_authorship::ProposerFactory::new(
- task_manager.spawn_handle(),
- client.clone(),
- transaction_pool.clone(),
- prometheus_registry.as_ref(),
- telemetry.as_ref().map(|x| x.handle()),
- );
+ if role.is_authority() {
+ let proposer = sc_basic_authorship::ProposerFactory::new(
+ task_manager.spawn_handle(),
+ client.clone(),
+ transaction_pool.clone(),
+ prometheus_registry.as_ref(),
+ telemetry.as_ref().map(|x| x.handle()),
+ );
- // If instant seal is requested, we just start it. Otherwise, we do the full PoW setup.
- if instant_seal {
- let params = sc_consensus_manual_seal::InstantSealParams {
- block_import: client.clone(),
- env: proposer,
- client,
- pool: transaction_pool,
- select_chain,
- consensus_data_provider: None,
- create_inherent_data_providers: move |_, ()| async move {
- Ok(sp_timestamp::InherentDataProvider::from_system_time())
- },
- };
+ // If instant seal is requested, we just start it. Otherwise, we do the full PoW setup.
+ if instant_seal {
+ let params = sc_consensus_manual_seal::InstantSealParams {
+ block_import: client.clone(),
+ env: proposer,
+ client,
+ pool: transaction_pool,
+ select_chain,
+ consensus_data_provider: None,
+ create_inherent_data_providers: move |_, ()| async move {
+ Ok(sp_timestamp::InherentDataProvider::from_system_time())
+ },
+ };
- let authorship_future = sc_consensus_manual_seal::run_instant_seal(params);
+ let authorship_future = sc_consensus_manual_seal::run_instant_seal(params);
- task_manager.spawn_essential_handle().spawn_blocking(
- "instant-seal",
- None,
- authorship_future,
- );
- } else {
- let (mining_worker, mining_worker_task) = sc_consensus_pow::start_mining_worker(
- Box::new(pow_block_import),
- client.clone(),
- select_chain,
- MultiPow::new(client, fork_config),
- proposer,
- sync_service.clone(),
- sync_service,
- // Note the mining algorithm in the pre-runtime digest.
- // This allows us to know which algo it was in the runtime.
- // TODO This also makes it possible to remove the algo info from
- // the seal.
- Some(PreDigest::from((sr25519_public_key.into(), mining_algo)).encode()),
- // This code is copied from above. Would be better to not repeat it.
- move |_, ()| async move {
- let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
- // set default `author` following miner specified in CLI
- let author = academy_pow_runtime::block_author::InherentDataProvider(
- sr25519_public_key.encode(),
- );
+ task_manager.spawn_essential_handle().spawn_blocking(
+ "instant-seal",
+ None,
+ authorship_future,
+ );
+ } else {
+ let (mining_worker, mining_worker_task) = sc_consensus_pow::start_mining_worker(
+ Box::new(pow_block_import),
+ client.clone(),
+ select_chain,
+ MultiPow::new(client, fork_config),
+ proposer,
+ sync_service.clone(),
+ sync_service,
+ // Note the mining algorithm in the pre-runtime digest.
+ // This allows us to know which algo it was in the runtime.
+ // TODO This also makes it possible to remove the algo info from
+ // the seal.
+ Some(PreDigest::from((sr25519_public_key.into(), mining_algo)).encode()),
+ // This code is copied from above. Would be better to not repeat it.
+ move |_, ()| async move {
+ let timestamp = sp_timestamp::InherentDataProvider::from_system_time();
+ // set default `author` following miner specified in CLI
+ let author = academy_pow_runtime::block_author::InherentDataProvider(
+ sr25519_public_key.encode(),
+ );
- Ok((timestamp, author))
- },
- std::time::Duration::from_secs(10),
- std::time::Duration::from_secs(5),
- );
+ Ok((timestamp, author))
+ },
+ std::time::Duration::from_secs(10),
+ std::time::Duration::from_secs(5),
+ );
- task_manager.spawn_essential_handle().spawn_blocking(
- "pow-miner",
- Some("pow-mining"),
- mining_worker_task,
- );
+ task_manager.spawn_essential_handle().spawn_blocking(
+ "pow-miner",
+ Some("pow-mining"),
+ mining_worker_task,
+ );
- // Start Mining worker.
- //TODO Some of this should move into the multi_pow crate.
- use multi_pow::{multi_hash_meets_difficulty, Compute};
- use sp_core::U256;
- let mut nonce: U256 = U256::from(0);
- std::thread::spawn(move || loop {
- let worker = mining_worker.clone();
- let metadata = worker.metadata();
- if let Some(metadata) = metadata {
- let compute = Compute {
- difficulty: metadata.difficulty,
- pre_hash: metadata.pre_hash,
- nonce,
- };
- let seal = compute.compute(mining_algo);
- if multi_hash_meets_difficulty(&seal.work, seal.difficulty) {
- nonce = U256::from(0);
- let _ = futures::executor::block_on(worker.submit(seal.encode()));
- } else {
- nonce = nonce.saturating_add(U256::from(1));
- if nonce == U256::MAX {
- nonce = U256::from(0);
- }
- }
- } else {
- std::thread::sleep(std::time::Duration::from_secs(1));
- }
- });
- }
- }
+ // Start Mining worker.
+ //TODO Some of this should move into the multi_pow crate.
+ use multi_pow::{multi_hash_meets_difficulty, Compute};
+ use sp_core::U256;
+ let mut nonce: U256 = U256::from(0);
+ std::thread::spawn(move || loop {
+ let worker = mining_worker.clone();
+ let metadata = worker.metadata();
+ if let Some(metadata) = metadata {
+ let compute = Compute {
+ difficulty: metadata.difficulty,
+ pre_hash: metadata.pre_hash,
+ nonce,
+ };
+ let seal = compute.compute(mining_algo);
+ if multi_hash_meets_difficulty(&seal.work, seal.difficulty) {
+ nonce = U256::from(0);
+ let _ = futures::executor::block_on(worker.submit(seal.encode()));
+ } else {
+ nonce = nonce.saturating_add(U256::from(1));
+ if nonce == U256::MAX {
+ nonce = U256::from(0);
+ }
+ }
+ } else {
+ std::thread::sleep(std::time::Duration::from_secs(1));
+ }
+ });
+ }
+ }
- network_starter.start_network();
- Ok(task_manager)
+ network_starter.start_network();
+ Ok(task_manager)
}
diff --git a/runtime/build.rs b/runtime/build.rs
index 6db3d90..9029164 100644
--- a/runtime/build.rs
+++ b/runtime/build.rs
@@ -1,8 +1,8 @@
fn main() {
- #[cfg(feature = "std")]
- substrate_wasm_builder::WasmBuilder::new()
- .with_current_project()
- .export_heap_base()
- .import_memory()
- .build()
+ #[cfg(feature = "std")]
+ substrate_wasm_builder::WasmBuilder::new()
+ .with_current_project()
+ .export_heap_base()
+ .import_memory()
+ .build()
}
diff --git a/runtime/src/block_author.rs b/runtime/src/block_author.rs
index b147eeb..156e630 100644
--- a/runtime/src/block_author.rs
+++ b/runtime/src/block_author.rs
@@ -14,142 +14,142 @@ use sp_std::vec::Vec;
#[frame_support::pallet(dev_mode)]
pub mod pallet {
- use frame_support::pallet_prelude::*;
- use frame_system::pallet_prelude::*;
-
- use super::*;
-
- /// The BlockAuthor Inherent pallet.
- #[pallet::pallet]
- pub struct Pallet<T>(PhantomData<T>);
- /// The pallet's configuration trait. Nothing to configure.
- #[pallet::config]
- pub trait Config: frame_system::Config {
- fn on_author_set(_author_account: Self::AccountId) {}
- }
-
- #[pallet::error]
- pub enum Error<T> {
- /// Author already set in block.
- AuthorAlreadySet,
- }
-
- /// Author of current block.
- #[pallet::storage]
- pub type Author<T: Config> = StorageValue<_, sr25519::Public, OptionQuery>;
-
- #[pallet::call]
- impl<T: Config> Pallet<T>
- where
- <T as frame_system::Config>::AccountId: From<sp_core::sr25519::Public>,
- {
- /// Inherent to set the author of a block
- #[pallet::weight(1_000_000)]
- pub fn set_author(origin: OriginFor<T>, author: sr25519::Public) -> DispatchResult {
- ensure_none(origin)?;
- ensure!(Author::<T>::get().is_none(), Error::<T>::AuthorAlreadySet);
-
- // Store the author in case other pallets want to fetch it and to let
- // offchain tools inspect it
- Author::<T>::put(author);
-
- // Call the hook
- T::on_author_set(author.into());
-
- Ok(())
- }
- }
-
- #[pallet::hooks]
- impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
- fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
- // Reset the author to None at the beginning of the block
- Author::<T>::kill();
-
- // Return zero weight because we are not using weight-based
- // transaction fees.
- Weight::zero()
- }
- }
-
- #[pallet::inherent]
- impl<T: Config> ProvideInherent for Pallet<T>
- where
- <T as frame_system::Config>::AccountId: From<sp_core::sr25519::Public>,
- {
- type Call = Call<T>;
- type Error = InherentError;
- const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
-
- fn is_inherent_required(_: &InherentData) -> Result<Option<Self::Error>, Self::Error> {
- // Return Ok(Some(_)) unconditionally because this inherent is required in every block
- // If it is not found, throw an AuthorInherentRequired error.
- Ok(Some(InherentError::Other(
- sp_runtime::RuntimeString::Borrowed("BlockAuthorInherentRequired"),
- )))
- }
-
- fn create_inherent(data: &InherentData) -> Option<Self::Call> {
- // Grab the Vec<u8> labelled with "author_" from the map of all inherent data
- let author_raw = data
- .get_data::<InherentType>(&INHERENT_IDENTIFIER)
- .expect("Gets and decodes authorship inherent data")?;
-
- // Decode the Vec<u8> into an actual author
- let author = sr25519::Public::decode(&mut &author_raw[..])
- .expect("Decodes author raw inherent data");
-
- Some(Call::set_author { author })
- }
-
- fn is_inherent(call: &Self::Call) -> bool {
- matches!(call, Call::set_author { .. })
- }
- }
+ use frame_support::pallet_prelude::*;
+ use frame_system::pallet_prelude::*;
+
+ use super::*;
+
+ /// The BlockAuthor Inherent pallet.
+ #[pallet::pallet]
+ pub struct Pallet<T>(PhantomData<T>);
+ /// The pallet's configuration trait. Nothing to configure.
+ #[pallet::config]
+ pub trait Config: frame_system::Config {
+ fn on_author_set(_author_account: Self::AccountId) {}
+ }
+
+ #[pallet::error]
+ pub enum Error<T> {
+ /// Author already set in block.
+ AuthorAlreadySet,
+ }
+
+ /// Author of current block.
+ #[pallet::storage]
+ pub type Author<T: Config> = StorageValue<_, sr25519::Public, OptionQuery>;
+
+ #[pallet::call]
+ impl<T: Config> Pallet<T>
+ where
+ <T as frame_system::Config>::AccountId: From<sp_core::sr25519::Public>,
+ {
+ /// Inherent to set the author of a block
+ #[pallet::weight(1_000_000)]
+ pub fn set_author(origin: OriginFor<T>, author: sr25519::Public) -> DispatchResult {
+ ensure_none(origin)?;
+ ensure!(Author::<T>::get().is_none(), Error::<T>::AuthorAlreadySet);
+
+ // Store the author in case other pallets want to fetch it and to let
+ // offchain tools inspect it
+ Author::<T>::put(author);
+
+ // Call the hook
+ T::on_author_set(author.into());
+
+ Ok(())
+ }
+ }
+
+ #[pallet::hooks]
+ impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
+ fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
+ // Reset the author to None at the beginning of the block
+ Author::<T>::kill();
+
+ // Return zero weight because we are not using weight-based
+ // transaction fees.
+ Weight::zero()
+ }
+ }
+
+ #[pallet::inherent]
+ impl<T: Config> ProvideInherent for Pallet<T>
+ where
+ <T as frame_system::Config>::AccountId: From<sp_core::sr25519::Public>,
+ {
+ type Call = Call<T>;
+ type Error = InherentError;
+ const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
+
+ fn is_inherent_required(_: &InherentData) -> Result<Option<Self::Error>, Self::Error> {
+ // Return Ok(Some(_)) unconditionally because this inherent is required in every block
+ // If it is not found, throw an AuthorInherentRequired error.
+ Ok(Some(InherentError::Other(sp_runtime::RuntimeString::Borrowed(
+ "BlockAuthorInherentRequired",
+ ))))
+ }
+
+ fn create_inherent(data: &InherentData) -> Option<Self::Call> {
+ // Grab the Vec<u8> labelled with "author_" from the map of all inherent data
+ let author_raw = data
+ .get_data::<InherentType>(&INHERENT_IDENTIFIER)
+ .expect("Gets and decodes authorship inherent data")?;
+
+ // Decode the Vec<u8> into an actual author
+ let author = sr25519::Public::decode(&mut &author_raw[..])
+ .expect("Decodes author raw inherent data");
+
+ Some(Call::set_author { author })
+ }
+
+ fn is_inherent(call: &Self::Call) -> bool {
+ matches!(call, Call::set_author { .. })
+ }
+ }
}
/// A trait to find the author (miner) of the block.
pub trait BlockAuthor {
- fn block_author() -> Option<sr25519::Public>;
+ fn block_author() -> Option<sr25519::Public>;
}
impl BlockAuthor for () {
- fn block_author() -> Option<sr25519::Public> {
- None
- }
+ fn block_author() -> Option<sr25519::Public> {
+ None
+ }
}
impl<T: Config> BlockAuthor for Pallet<T> {
- fn block_author() -> Option<sr25519::Public> {
- Author::<T>::get()
- }
+ fn block_author() -> Option<sr25519::Public> {
+ Author::<T>::get()
+ }
}
pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"author__";
#[derive(Encode, Decode, Debug)]
pub enum InherentError {
- Other(RuntimeString),
+ Other(RuntimeString),
}
impl IsFatalError for InherentError {
- fn is_fatal_error(&self) -> bool {
- match *self {
- InherentError::Other(_) => true,
- }
- }
+ fn is_fatal_error(&self) -> bool {
+ match *self {
+ InherentError::Other(_) => true,
+ }
+ }
}
impl InherentError {
- /// Try to create an instance ouf of the given identifier and data.
- #[cfg(feature = "std")]
- pub fn try_from(id: &InherentIdentifier, data: &[u8]) -> Option<Self> {
- if id == &INHERENT_IDENTIFIER {
- <InherentError as parity_scale_codec::Decode>::decode(&mut &data[..]).ok()
- } else {
- None
- }
- }
+ /// Try to create an instance ouf of the given identifier and data.
+ #[cfg(feature = "std")]
+ pub fn try_from(id: &InherentIdentifier, data: &[u8]) -> Option<Self> {
+ if id == &INHERENT_IDENTIFIER {
+ <InherentError as parity_scale_codec::Decode>::decode(&mut &data[..]).ok()
+ } else {
+ None
+ }
+ }
}
/// The type of data that the inherent will contain.
@@ -162,26 +162,26 @@ pub struct InherentDataProvider(pub InherentType);
#[cfg(feature = "std")]
#[async_trait::async_trait]
impl sp_inherents::InherentDataProvider for InherentDataProvider {
- async fn provide_inherent_data(
- &self,
- inherent_data: &mut InherentData,
- ) -> Result<(), sp_inherents::Error> {
- inherent_data.put_data(INHERENT_IDENTIFIER, &self.0)
- }
-
- async fn try_handle_error(
- &self,
- identifier: &InherentIdentifier,
- _error: &[u8],
- ) -> Option<Result<(), sp_inherents::Error>> {
- // Dont' process modules from other inherents
- if *identifier != INHERENT_IDENTIFIER {
- return None;
- }
-
- // All errors with the author inehrent are fatal
- Some(Err(sp_inherents::Error::Application(Box::from(
- String::from("Error processing author inherent"),
- ))))
- }
+ async fn provide_inherent_data(
+ &self,
+ inherent_data: &mut InherentData,
+ ) -> Result<(), sp_inherents::Error> {
+ inherent_data.put_data(INHERENT_IDENTIFIER, &self.0)
+ }
+
+ async fn try_handle_error(
+ &self,
+ identifier: &InherentIdentifier,
+ _error: &[u8],
+ ) -> Option<Result<(), sp_inherents::Error>> {
+ // Dont' process modules from other inherents
+ if *identifier != INHERENT_IDENTIFIER {
+ return None;
+ }
+
+ // All errors with the author inehrent are fatal
+ Some(Err(sp_inherents::Error::Application(Box::from(String::from(
+ "Error processing author inherent",
+ )))))
+ }
}
diff --git a/runtime/src/difficulty.rs b/runtime/src/difficulty.rs
index babfeab..650d77a 100644
--- a/runtime/src/difficulty.rs
+++ b/runtime/src/difficulty.rs
@@ -15,18 +15,18 @@ use sp_runtime::traits::UniqueSaturatedInto;
#[derive(Encode, Decode, Clone, Copy, Eq, PartialEq, Debug, MaxEncodedLen, TypeInfo)]
pub struct DifficultyAndTimestamp<M> {
- pub difficulty: Difficulty,
- pub timestamp: M,
+ pub difficulty: Difficulty,
+ pub timestamp: M,
}
/// Move value linearly toward a goal
pub fn damp(actual: u128, goal: u128, damp_factor: u128) -> u128 {
- (actual + (damp_factor - 1) * goal) / damp_factor
+ (actual + (damp_factor - 1) * goal) / damp_factor
}
/// Limit value to be within some factor from a goal
pub fn clamp(actual: u128, goal: u128, clamp_factor: u128) -> u128 {
- max(goal / clamp_factor, min(actual, goal * clamp_factor))
+ max(goal / clamp_factor, min(actual, goal * clamp_factor))
}
const DIFFICULTY_ADJUST_WINDOW: u128 = 60;
@@ -36,168 +36,166 @@ pub use pallet::*;
#[frame_support::pallet(dev_mode)]
pub mod pallet {
- use frame_support::pallet_prelude::*;
- use frame_system::pallet_prelude::*;
-
- use super::*;
-
- /// Pallet's configuration trait.
- #[pallet::config]
- pub trait Config<I: 'static = ()>: frame_system::Config {
- /// A Source for timestamp data
- type TimeProvider: Time;
- /// The block time that the DAA will attempt to maintain
- type TargetBlockTime: Get<u128>;
- /// Dampening factor to use for difficulty adjustment
- type DampFactor: Get<u128>;
- /// Clamp factor to use for difficulty adjustment
- /// Limit value to within this factor of goal. Recommended value: 2
- type ClampFactor: Get<u128>;
- /// The maximum difficulty allowed. Recommended to use u128::max_value()
- type MaxDifficulty: Get<u128>;
- /// Minimum difficulty, enforced in difficulty retargetting
- /// avoids getting stuck when trying to increase difficulty subject to dampening
- /// Recommended to use same value as DampFactor
- type MinDifficulty: Get<u128>;
-
- /// Now that the pallet is instantiable, we need a way to decide which blocks are
- /// relevant to this instance. This function does just that.
- ///
- /// The default implementation assumes that all blocks are relevant which is what
- /// you probably want when there is only a single instance.
- fn relevant_to_this_instance() -> bool {
- true
- }
- }
-
- #[pallet::pallet]
- pub struct Pallet<T, I = ()>(_);
-
- type DifficultyList<T, I> =
- [Option<DifficultyAndTimestamp<<<T as Config<I>>::TimeProvider as Time>::Moment>>; 60];
-
- /// Past difficulties and timestamps, from earliest to latest.
- #[pallet::storage]
- pub type PastDifficultiesAndTimestamps<T: Config<I>, I: 'static = ()> =
- StorageValue<_, DifficultyList<T, I>, ValueQuery, EmptyList<T, I>>;
-
- pub struct EmptyList<T: Config<I>, I: 'static = ()>(PhantomData<(T, I)>);
- impl<T: Config<I>, I> Get<DifficultyList<T, I>> for EmptyList<T, I> {
- fn get() -> DifficultyList<T, I> {
- [None; DIFFICULTY_ADJUST_WINDOW as usize]
- }
- }
-
- /// Current difficulty.
- #[pallet::storage]
- #[pallet::getter(fn difficulty)]
- pub type CurrentDifficulty<T: Config<I>, I: 'static = ()> =
- StorageValue<_, Difficulty, ValueQuery>;
-
- /// Initial difficulty.
- #[pallet::storage]
- pub type InitialDifficulty<T: Config<I>, I: 'static = ()> =
- StorageValue<_, Difficulty, ValueQuery>;
-
- #[pallet::genesis_config]
- pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
- pub _ph_data: Option<PhantomData<(T, I)>>,
- pub initial_difficulty: [u8; 32], // Difficulty = U256
- }
-
- #[pallet::genesis_build]
- impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
- fn build(&self) {
- let initial_difficulty = U256::from_little_endian(&self.initial_difficulty);
- // Initialize the Current difficulty
- CurrentDifficulty::<T, I>::put(&initial_difficulty);
-
- // Store the initial difficulty in storage because we will need it
- // during the first DIFFICULTY_ADJUSTMENT_WINDOW blocks (see todo below).
- InitialDifficulty::<T, I>::put(&initial_difficulty);
- }
- }
-
- impl<T: Config<I>, I: 'static> Default for GenesisConfig<T, I> {
- fn default() -> Self {
- GenesisConfig {
- _ph_data: Default::default(),
- initial_difficulty: [0u8; 32],
- }
- }
- }
-
- #[pallet::hooks]
- impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
- fn on_finalize(_n: BlockNumberFor<T>) {
- // First check if this is block is relevant to this instance of the difficulty adjustment algorithm
- if !T::relevant_to_this_instance() {
- return;
- }
-
- let mut data = PastDifficultiesAndTimestamps::<T, I>::get();
-
- for i in 1..data.len() {
- data[i - 1] = data[i];
- }
-
- data[data.len() - 1] = Some(DifficultyAndTimestamp {
- timestamp: T::TimeProvider::now(),
- difficulty: Self::difficulty(),
- });
-
- let mut ts_delta = 0;
- for i in 1..(DIFFICULTY_ADJUST_WINDOW as usize) {
- let prev: Option<u128> = data[i - 1].map(|d| d.timestamp.unique_saturated_into());
- let cur: Option<u128> = data[i].map(|d| d.timestamp.unique_saturated_into());
-
- let delta = match (prev, cur) {
- (Some(prev), Some(cur)) => cur.saturating_sub(prev),
- _ => T::TargetBlockTime::get(),
- };
- ts_delta += delta;
- }
-
- if ts_delta == 0 {
- ts_delta = 1;
- }
-
- let mut diff_sum = U256::zero();
- //TODO Could we just initialize every array cell to the initial difficulty to not need the
- // separate storage item?
- for item in data.iter().take(DIFFICULTY_ADJUST_WINDOW as usize) {
- let diff = match item.map(|d| d.difficulty) {
- Some(diff) => diff,
- None => InitialDifficulty::<T, I>::get(),
- };
- diff_sum += diff;
- }
-
- if diff_sum < U256::from(T::MinDifficulty::get()) {
- diff_sum = U256::from(T::MinDifficulty::get());
- }
-
- // Calculate the average length of the adjustment window
- let adjustment_window = DIFFICULTY_ADJUST_WINDOW * T::TargetBlockTime::get();
-
- // adjust time delta toward goal subject to dampening and clamping
- let adj_ts = clamp(
- damp(ts_delta, adjustment_window, T::DampFactor::get()),
- adjustment_window,
- T::ClampFactor::get(),
- );
-
- // minimum difficulty avoids getting stuck due to dampening
- let difficulty = min(
- U256::from(T::MaxDifficulty::get()),
- max(
- U256::from(T::MinDifficulty::get()),
- diff_sum * U256::from(T::TargetBlockTime::get()) / U256::from(adj_ts),
- ),
- );
-
- <PastDifficultiesAndTimestamps<T, I>>::put(data);
- <CurrentDifficulty<T, I>>::put(difficulty);
- }
- }
+ use frame_support::pallet_prelude::*;
+ use frame_system::pallet_prelude::*;
+
+ use super::*;
+
+ /// Pallet's configuration trait.
+ #[pallet::config]
+ pub trait Config<I: 'static = ()>: frame_system::Config {
+ /// A Source for timestamp data
+ type TimeProvider: Time;
+ /// The block time that the DAA will attempt to maintain
+ type TargetBlockTime: Get<u128>;
+ /// Dampening factor to use for difficulty adjustment
+ type DampFactor: Get<u128>;
+ /// Clamp factor to use for difficulty adjustment
+ /// Limit value to within this factor of goal. Recommended value: 2
+ type ClampFactor: Get<u128>;
+ /// The maximum difficulty allowed. Recommended to use u128::max_value()
+ type MaxDifficulty: Get<u128>;
+ /// Minimum difficulty, enforced in difficulty retargetting
+ /// avoids getting stuck when trying to increase difficulty subject to dampening
+ /// Recommended to use same value as DampFactor
+ type MinDifficulty: Get<u128>;
+
+ /// Now that the pallet is instantiable, we need a way to decide which blocks are
+ /// relevant to this instance. This function does just that.
+ ///
+ /// The default implementation assumes that all blocks are relevant which is what
+ /// you probably want when there is only a single instance.
+ fn relevant_to_this_instance() -> bool {
+ true
+ }
+ }
+
+ #[pallet::pallet]
+ pub struct Pallet<T, I = ()>(_);
+
+ type DifficultyList<T, I> =
+ [Option<DifficultyAndTimestamp<<<T as Config<I>>::TimeProvider as Time>::Moment>>; 60];
+
+ /// Past difficulties and timestamps, from earliest to latest.
+ #[pallet::storage]
+ pub type PastDifficultiesAndTimestamps<T: Config<I>, I: 'static = ()> =
+ StorageValue<_, DifficultyList<T, I>, ValueQuery, EmptyList<T, I>>;
+
+ pub struct EmptyList<T: Config<I>, I: 'static = ()>(PhantomData<(T, I)>);
+ impl<T: Config<I>, I> Get<DifficultyList<T, I>> for EmptyList<T, I> {
+ fn get() -> DifficultyList<T, I> {
+ [None; DIFFICULTY_ADJUST_WINDOW as usize]
+ }
+ }
+
+ /// Current difficulty.
+ #[pallet::storage]
+ #[pallet::getter(fn difficulty)]
+ pub type CurrentDifficulty<T: Config<I>, I: 'static = ()> =
+ StorageValue<_, Difficulty, ValueQuery>;
+
+ /// Initial difficulty.
+ #[pallet::storage]
+ pub type InitialDifficulty<T: Config<I>, I: 'static = ()> =
+ StorageValue<_, Difficulty, ValueQuery>;
+
+ #[pallet::genesis_config]
+ pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
+ pub _ph_data: Option<PhantomData<(T, I)>>,
+ pub initial_difficulty: [u8; 32], // Difficulty = U256
+ }
+
+ #[pallet::genesis_build]
+ impl<T: Config<I>, I: 'static> BuildGenesisConfig for GenesisConfig<T, I> {
+ fn build(&self) {
+ let initial_difficulty = U256::from_little_endian(&self.initial_difficulty);
+ // Initialize the Current difficulty
+ CurrentDifficulty::<T, I>::put(&initial_difficulty);
+
+ // Store the initial difficulty in storage because we will need it
+ // during the first DIFFICULTY_ADJUSTMENT_WINDOW blocks (see todo below).
+ InitialDifficulty::<T, I>::put(&initial_difficulty);
+ }
+ }
+
+ impl<T: Config<I>, I: 'static> Default for GenesisConfig<T, I> {
+ fn default() -> Self {
+ GenesisConfig { _ph_data: Default::default(), initial_difficulty: [0u8; 32] }
+ }
+ }
+
+ #[pallet::hooks]
+ impl<T: Config<I>, I: 'static> Hooks<BlockNumberFor<T>> for Pallet<T, I> {
+ fn on_finalize(_n: BlockNumberFor<T>) {
+ // First check if this is block is relevant to this instance of the difficulty
+ // adjustment algorithm
+ if !T::relevant_to_this_instance() {
+ return;
+ }
+
+ let mut data = PastDifficultiesAndTimestamps::<T, I>::get();
+
+ for i in 1..data.len() {
+ data[i - 1] = data[i];
+ }
+
+ data[data.len() - 1] = Some(DifficultyAndTimestamp {
+ timestamp: T::TimeProvider::now(),
+ difficulty: Self::difficulty(),
+ });
+
+ let mut ts_delta = 0;
+ for i in 1..(DIFFICULTY_ADJUST_WINDOW as usize) {
+ let prev: Option<u128> = data[i - 1].map(|d| d.timestamp.unique_saturated_into());
+ let cur: Option<u128> = data[i].map(|d| d.timestamp.unique_saturated_into());
+
+ let delta = match (prev, cur) {
+ (Some(prev), Some(cur)) => cur.saturating_sub(prev),
+ _ => T::TargetBlockTime::get(),
+ };
+ ts_delta += delta;
+ }
+
+ if ts_delta == 0 {
+ ts_delta = 1;
+ }
+
+ let mut diff_sum = U256::zero();
+ //TODO Could we just initialize every array cell to the initial difficulty to not need
+ // the separate storage item?
+ for item in data.iter().take(DIFFICULTY_ADJUST_WINDOW as usize) {
+ let diff = match item.map(|d| d.difficulty) {
+ Some(diff) => diff,
+ None => InitialDifficulty::<T, I>::get(),
+ };
+ diff_sum += diff;
+ }
+
+ if diff_sum < U256::from(T::MinDifficulty::get()) {
+ diff_sum = U256::from(T::MinDifficulty::get());
+ }
+
+ // Calculate the average length of the adjustment window
+ let adjustment_window = DIFFICULTY_ADJUST_WINDOW * T::TargetBlockTime::get();
+
+ // adjust time delta toward goal subject to dampening and clamping
+ let adj_ts = clamp(
+ damp(ts_delta, adjustment_window, T::DampFactor::get()),
+ adjustment_window,
+ T::ClampFactor::get(),
+ );
+
+ // minimum difficulty avoids getting stuck due to dampening
+ let difficulty = min(
+ U256::from(T::MaxDifficulty::get()),
+ max(
+ U256::from(T::MinDifficulty::get()),
+ diff_sum * U256::from(T::TargetBlockTime::get()) / U256::from(adj_ts),
+ ),
+ );
+
+ <PastDifficultiesAndTimestamps<T, I>>::put(data);
+ <CurrentDifficulty<T, I>>::put(difficulty);
+ }
+ }
}
diff --git a/runtime/src/issuance.rs b/runtime/src/issuance.rs
index 9a11f9d..a8146c1 100644
--- a/runtime/src/issuance.rs
+++ b/runtime/src/issuance.rs
@@ -3,26 +3,28 @@
/// A trait for types that can provide the amount of issuance to award to the block
/// author for the given block number.
pub trait Issuance<BlockNumber, Balance> {
- fn issuance(block: BlockNumber) -> Balance;
+ fn issuance(block: BlockNumber) -> Balance;
}
// Minimal implementations for when you don't actually want any issuance
impl Issuance<u32, u128> for () {
- fn issuance(_block: u32) -> u128 {
- 0
- }
+ fn issuance(_block: u32) -> u128 {
+ 0
+ }
}
impl Issuance<u64, u128> for () {
- fn issuance(_block: u64) -> u128 {
- 0
- }
+ fn issuance(_block: u64) -> u128 {
+ 0
+ }
}
/// A type that provides block issuance according to bitcoin's rules
/// Initial issuance is 50 / block
/// Issuance is cut in half every 210,000 blocks
-/// cribbed from github.com/Bitcoin-ABC/bitcoin-abc/blob/9c7b12e6f128a59423f4de3d6d4b5231ebe9aac2/src/validation.cpp#L1007
+/// cribbed from
+/// github.com/Bitcoin-ABC/bitcoin-abc/blob/9c7b12e6f128a59423f4de3d6d4b5231ebe9aac2/src/validation.
+/// cpp#L1007
pub struct BitcoinHalving;
@@ -32,16 +34,16 @@ const HALVING_INTERVAL: u32 = 210_000;
const INITIAL_ISSUANCE: u32 = 50;
impl Issuance<u32, u128> for BitcoinHalving {
- fn issuance(block: u32) -> u128 {
- let halving = block / HALVING_INTERVAL;
- // Force block reward to zero when right shift is undefined.
- if halving >= 64 {
- return 0;
- }
-
- // Approximately, 600 seconds (or 10 minutes) for a block to be finalized.
- // Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years.
- // Divided by 2 using bitwise
- (INITIAL_ISSUANCE >> halving).into()
- }
+ fn issuance(block: u32) -> u128 {
+ let halving = block / HALVING_INTERVAL;
+ // Force block reward to zero when right shift is undefined.
+ if halving >= 64 {
+ return 0;
+ }
+
+ // Approximately, 600 seconds (or 10 minutes) for a block to be finalized.
+ // Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years.
+ // Divided by 2 using bitwise
+ (INITIAL_ISSUANCE >> halving).into()
+ }
}
diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
index 5b09407..b724b7e 100644
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -9,24 +9,24 @@
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
pub use frame_support::{
- construct_runtime, parameter_types, derive_impl,
- traits::{
- Currency, EstimateNextNewSession, Imbalance, IsSubType, KeyOwnerProofSystem,
- LockIdentifier, Nothing, OnUnbalanced, ValidatorSet, VariantCountOf,
- },
- weights::{
- constants::{
- BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND,
- },
- IdentityFee, Weight,
- },
- Callable, StorageValue,
+ construct_runtime, derive_impl, parameter_types,
+ traits::{
+ Currency, EstimateNextNewSession, Imbalance, IsSubType, KeyOwnerProofSystem,
+ LockIdentifier, Nothing, OnUnbalanced, ValidatorSet, VariantCountOf,
+ },
+ weights::{
+ constants::{
+ BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND,
+ },
+ IdentityFee, Weight,
+ },
+ Callable, StorageValue,
};
use frame_support::{
- genesis_builder_helper::{build_state, get_preset},
- instances::{Instance1, Instance2, Instance3},
- sp_runtime::Perquintill,
- traits::{ConstU128, ConstU32, ConstU8},
+ genesis_builder_helper::{build_state, get_preset},
+ instances::{Instance1, Instance2, Instance3},
+ sp_runtime::Perquintill,
+ traits::{ConstU128, ConstU32, ConstU8},
};
use multi_pow::SupportedHashes;
pub use pallet_balances::Call as BalancesCall;
@@ -40,15 +40,14 @@ use sp_core::OpaqueMetadata;
#[cfg(any(feature = "std", test))]
pub use sp_runtime::BuildStorage;
use sp_runtime::{
- create_runtime_str, generic,
- traits::{
- AccountIdLookup, BlakeTwo256, Block as BlockT, Bounded, IdentifyAccount, One, Verify,
- },
- transaction_validity::{
- InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError,
- },
- ApplyExtrinsicResult, DigestItem, MultiSignature,
- ExtrinsicInclusionMode,
+ create_runtime_str, generic,
+ traits::{
+ AccountIdLookup, BlakeTwo256, Block as BlockT, Bounded, IdentifyAccount, One, Verify,
+ },
+ transaction_validity::{
+ InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError,
+ },
+ ApplyExtrinsicResult, DigestItem, ExtrinsicInclusionMode, MultiSignature,
};
pub use sp_runtime::{FixedPointNumber, Perbill, Permill};
use sp_std::prelude::*;
@@ -101,38 +100,35 @@ pub mod utxo;
/// of data like extrinsics, allowing for them to continue syncing the network through upgrades
/// to even the core data structures.
pub mod opaque {
- pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
+ pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic;
- use super::*;
+ use super::*;
- /// Opaque block header type.
- pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
- /// Opaque block type.
- pub type Block = generic::Block<Header, UncheckedExtrinsic>;
- /// Opaque block identifier type.
- pub type BlockId = generic::BlockId<Block>;
+ /// Opaque block header type.
+ pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
+ /// Opaque block type.
+ pub type Block = generic::Block<Header, UncheckedExtrinsic>;
+ /// Opaque block identifier type.
+ pub type BlockId = generic::BlockId<Block>;
}
/// This runtime version.
#[sp_version::runtime_version]
pub const VERSION: RuntimeVersion = RuntimeVersion {
- spec_name: create_runtime_str!("academy-pow"),
- impl_name: create_runtime_str!("academy-pow"),
- authoring_version: 1,
- spec_version: 1,
- impl_version: 1,
- apis: RUNTIME_API_VERSIONS,
- transaction_version: 1,
- state_version: 1,
+ spec_name: create_runtime_str!("academy-pow"),
+ impl_name: create_runtime_str!("academy-pow"),
+ authoring_version: 1,
+ spec_version: 1,
+ impl_version: 1,
+ apis: RUNTIME_API_VERSIONS,
+ transaction_version: 1,
+ state_version: 1,
};
/// The version information used to identify this runtime when compiled natively.
#[cfg(feature = "std")]
pub fn native_version() -> NativeVersion {
- NativeVersion {
- runtime_version: VERSION,
- can_author_with: Default::default(),
- }
+ NativeVersion { runtime_version: VERSION, can_author_with: Default::default() }
}
const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
@@ -142,205 +138,204 @@ pub const TOKEN_DECIMALS: u32 = 12;
pub const TOKEN: u128 = 10u128.pow(TOKEN_DECIMALS);
parameter_types! {
- pub const BlockHashCount: BlockNumber = 2400;
- pub const Version: RuntimeVersion = VERSION;
- /// We allow for 2 seconds of compute with a 6 second average block time.
- pub BlockWeights: frame_system::limits::BlockWeights =
- frame_system::limits::BlockWeights::with_sensible_defaults(
- Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX),
- NORMAL_DISPATCH_RATIO,
- );
- pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength
- ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);
- pub const SS58Prefix: u8 = 42;
+ pub const BlockHashCount: BlockNumber = 2400;
+ pub const Version: RuntimeVersion = VERSION;
+ /// We allow for 2 seconds of compute with a 6 second average block time.
+ pub BlockWeights: frame_system::limits::BlockWeights =
+ frame_system::limits::BlockWeights::with_sensible_defaults(
+ Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX),
+ NORMAL_DISPATCH_RATIO,
+ );
+ pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength
+ ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO);
+ pub const SS58Prefix: u8 = 42;
}
#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)]
impl frame_system::Config for Runtime {
- /// The basic call filter to use in dispatchable.
- type BaseCallFilter = frame_support::traits::Everything;
- /// Block & extrinsics weights: base values and limits.
- type BlockWeights = BlockWeights;
- /// The maximum length of a block (in bytes).
- type BlockLength = BlockLength;
- /// The identifier used to distinguish between accounts.
- type AccountId = AccountId;
- /// The aggregated dispatch type that is available for extrinsics.
- type RuntimeCall = RuntimeCall;
- /// The lookup mechanism to get account ID from whatever is passed in dispatchers.
- type Lookup = AccountIdLookup<AccountId, ()>;
- /// The type for hashing blocks and tries.
- type Hash = Hash;
- /// The hashing algorithm used.
- type Hashing = BlakeTwo256;
- /// The ubiquitous event type.
- type RuntimeEvent = RuntimeEvent;
- /// The ubiquitous origin type.
- type RuntimeOrigin = RuntimeOrigin;
- /// Maximum number of block number to block hash mappings to keep (oldest pruned first).
- type BlockHashCount = BlockHashCount;
- /// The weight of database operations that the runtime can invoke.
- type DbWeight = RocksDbWeight;
- /// Version of the runtime.
- type Version = Version;
- /// This type is being generated by the construct runtime macro.
- type PalletInfo = PalletInfo;
- /// What to do if a new account is created.
- type OnNewAccount = ();
- /// What to do if an account is fully reaped from the system.
- type OnKilledAccount = ();
- /// The data to be stored in an account.
- type AccountData = pallet_balances::AccountData<Balance>;
- /// Weight information for the extrinsics of this pallet.
- type SystemWeightInfo = ();
- /// This is used as an identifier of the chain. 42 is the generic substrate prefix.
- type SS58Prefix = SS58Prefix;
- /// The set code logic, just the default since we're not a parachain.
- type OnSetCode = ();
- type MaxConsumers = frame_support::traits::ConstU32<16>;
- type Nonce = Nonce;
- type Block = Block;
+ /// The basic call filter to use in dispatchable.
+ type BaseCallFilter = frame_support::traits::Everything;
+ /// Block & extrinsics weights: base values and limits.
+ type BlockWeights = BlockWeights;
+ /// The maximum length of a block (in bytes).
+ type BlockLength = BlockLength;
+ /// The identifier used to distinguish between accounts.
+ type AccountId = AccountId;
+ /// The aggregated dispatch type that is available for extrinsics.
+ type RuntimeCall = RuntimeCall;
+ /// The lookup mechanism to get account ID from whatever is passed in dispatchers.
+ type Lookup = AccountIdLookup<AccountId, ()>;
+ /// The type for hashing blocks and tries.
+ type Hash = Hash;
+ /// The hashing algorithm used.
+ type Hashing = BlakeTwo256;
+ /// The ubiquitous event type.
+ type RuntimeEvent = RuntimeEvent;
+ /// The ubiquitous origin type.
+ type RuntimeOrigin = RuntimeOrigin;
+ /// Maximum number of block number to block hash mappings to keep (oldest pruned first).
+ type BlockHashCount = BlockHashCount;
+ /// The weight of database operations that the runtime can invoke.
+ type DbWeight = RocksDbWeight;
+ /// Version of the runtime.
+ type Version = Version;
+ /// This type is being generated by the construct runtime macro.
+ type PalletInfo = PalletInfo;
+ /// What to do if a new account is created.
+ type OnNewAccount = ();
+ /// What to do if an account is fully reaped from the system.
+ type OnKilledAccount = ();
+ /// The data to be stored in an account.
+ type AccountData = pallet_balances::AccountData<Balance>;
+ /// Weight information for the extrinsics of this pallet.
+ type SystemWeightInfo = ();
+ /// This is used as an identifier of the chain. 42 is the generic substrate prefix.
+ type SS58Prefix = SS58Prefix;
+ /// The set code logic, just the default since we're not a parachain.
+ type OnSetCode = ();
+ type MaxConsumers = frame_support::traits::ConstU32<16>;
+ type Nonce = Nonce;
+ type Block = Block;
}
parameter_types! {
- pub const MinimumPeriod: u64 = 1000;
+ pub const MinimumPeriod: u64 = 1000;
}
impl pallet_timestamp::Config for Runtime {
- /// A timestamp: milliseconds since the unix epoch.
- type Moment = u64;
- type OnTimestampSet = ();
- type MinimumPeriod = MinimumPeriod;
- type WeightInfo = ();
+ /// A timestamp: milliseconds since the unix epoch.
+ type Moment = u64;
+ type OnTimestampSet = ();
+ type MinimumPeriod = MinimumPeriod;
+ type WeightInfo = ();
}
impl pallet_balances::Config for Runtime {
- type MaxLocks = ConstU32<50>;
- type MaxReserves = ();
- type ReserveIdentifier = [u8; 8];
- /// The type for recording an account's balance.
- type Balance = Balance;
- /// The ubiquitous event type.
- type RuntimeEvent = RuntimeEvent;
- type DustRemoval = ();
- type ExistentialDeposit = ConstU128<500>;
- type AccountStore = System;
- type WeightInfo = pallet_balances::weights::SubstrateWeight<Runtime>;
- type FreezeIdentifier = RuntimeFreezeReason;
- type MaxFreezes = VariantCountOf<RuntimeFreezeReason>;
- type RuntimeHoldReason = RuntimeHoldReason;
- type RuntimeFreezeReason = RuntimeFreezeReason;
+ type MaxLocks = ConstU32<50>;
+ type MaxReserves = ();
+ type ReserveIdentifier = [u8; 8];
+ /// The type for recording an account's balance.
+ type Balance = Balance;
+ /// The ubiquitous event type.
+ type RuntimeEvent = RuntimeEvent;
+ type DustRemoval = ();
+ type ExistentialDeposit = ConstU128<500>;
+ type AccountStore = System;
+ type WeightInfo = pallet_balances::weights::SubstrateWeight<Runtime>;
+ type FreezeIdentifier = RuntimeFreezeReason;
+ type MaxFreezes = VariantCountOf<RuntimeFreezeReason>;
+ type RuntimeHoldReason = RuntimeHoldReason;
+ type RuntimeFreezeReason = RuntimeFreezeReason;
}
parameter_types! {
- pub const TargetBlockTime: u128 = 5_000;
- // Setting min difficulty to damp factor per recommendation
- pub const DampFactor: u128 = 3;
- pub const ClampFactor: u128 = 2;
- pub const MaxDifficulty: u128 = u128::max_value();
+ pub const TargetBlockTime: u128 = 5_000;
+ // Setting min difficulty to damp factor per recommendation
+ pub const DampFactor: u128 = 3;
+ pub const ClampFactor: u128 = 2;
+ pub const MaxDifficulty: u128 = u128::max_value();
}
// Helper function to get the current blocks PoW algo from the predigest
fn current_blocks_mining_algo() -> SupportedHashes {
- System::digest()
- .logs
- .iter()
- .find_map(|digest_item| match digest_item {
- DigestItem::PreRuntime(POW_ENGINE_ID, pre_digest) => {
- PreDigest::decode(&mut &pre_digest[..]).map(|d| d.1).ok()
- }
- _ => None,
- })
- .expect("There should be exactly one pow pre-digest item")
+ System::digest()
+ .logs
+ .iter()
+ .find_map(|digest_item| match digest_item {
+ DigestItem::PreRuntime(POW_ENGINE_ID, pre_digest) =>
+ PreDigest::decode(&mut &pre_digest[..]).map(|d| d.1).ok(),
+ _ => None,
+ })
+ .expect("There should be exactly one pow pre-digest item")
}
impl difficulty::Config<Instance1> for Runtime {
- type TimeProvider = Timestamp;
- type TargetBlockTime = TargetBlockTime;
- type DampFactor = DampFactor;
- type ClampFactor = ClampFactor;
- type MaxDifficulty = MaxDifficulty;
- type MinDifficulty = DampFactor;
-
- fn relevant_to_this_instance() -> bool {
- current_blocks_mining_algo() == SupportedHashes::Md5
- }
+ type TimeProvider = Timestamp;
+ type TargetBlockTime = TargetBlockTime;
+ type DampFactor = DampFactor;
+ type ClampFactor = ClampFactor;
+ type MaxDifficulty = MaxDifficulty;
+ type MinDifficulty = DampFactor;
+
+ fn relevant_to_this_instance() -> bool {
+ current_blocks_mining_algo() == SupportedHashes::Md5
+ }
}
impl difficulty::Config<Instance2> for Runtime {
- type TimeProvider = Timestamp;
- type TargetBlockTime = TargetBlockTime;
- type DampFactor = DampFactor;
- type ClampFactor = ClampFactor;
- type MaxDifficulty = MaxDifficulty;
- type MinDifficulty = DampFactor;
-
- fn relevant_to_this_instance() -> bool {
- current_blocks_mining_algo() == SupportedHashes::Sha3
- }
+ type TimeProvider = Timestamp;
+ type TargetBlockTime = TargetBlockTime;
+ type DampFactor = DampFactor;
+ type ClampFactor = ClampFactor;
+ type MaxDifficulty = MaxDifficulty;
+ type MinDifficulty = DampFactor;
+
+ fn relevant_to_this_instance() -> bool {
+ current_blocks_mining_algo() == SupportedHashes::Sha3
+ }
}
impl difficulty::Config<Instance3> for Runtime {
- type TimeProvider = Timestamp;
- type TargetBlockTime = TargetBlockTime;
- type DampFactor = DampFactor;
- type ClampFactor = ClampFactor;
- type MaxDifficulty = MaxDifficulty;
- type MinDifficulty = DampFactor;
-
- fn relevant_to_this_instance() -> bool {
- current_blocks_mining_algo() == SupportedHashes::Keccak
- }
+ type TimeProvider = Timestamp;
+ type TargetBlockTime = TargetBlockTime;
+ type DampFactor = DampFactor;
+ type ClampFactor = ClampFactor;
+ type MaxDifficulty = MaxDifficulty;
+ type MinDifficulty = DampFactor;
+
+ fn relevant_to_this_instance() -> bool {
+ current_blocks_mining_algo() == SupportedHashes::Keccak
+ }
}
impl block_author::Config for Runtime {
- // Each block mined issues 50 new tokens to the miner
- fn on_author_set(author_account: Self::AccountId) {
- let issuance = 50 * TOKEN;
- let _ = Balances::deposit_creating(&author_account, issuance);
- }
+ // Each block mined issues 50 new tokens to the miner
+ fn on_author_set(author_account: Self::AccountId) {
+ let issuance = 50 * TOKEN;
+ let _ = Balances::deposit_creating(&author_account, issuance);
+ }
}
parameter_types! {
- // This value increases the priority of `Operational` transactions by adding
- // a "virtual tip" that's equal to the `OperationalFeeMultiplier * final_fee`.
- // follows polkadot : https://github.com/paritytech/polkadot/blob/9ce5f7ef5abb1a4291454e8c9911b304d80679f9/runtime/polkadot/src/lib.rs#L369
- pub const OperationalFeeMultiplier: u8 = 5;
- // We expect that on average 25% of the normal capacity will be occupied with normal txs.
- pub const TargetSaturationLevel: Perquintill = Perquintill::from_percent(25);
- // During 20 blocks the fee may not change more than by 100%. This, together with the
- // `TargetSaturationLevel` value, results in variability ~0.067. For the corresponding
- // formulas please refer to Substrate code at `frame/transaction-payment/src/lib.rs`.
- pub FeeVariability: Multiplier = Multiplier::saturating_from_rational(67, 1000);
- // Fee should never be lower than the computational cost.
- pub MinimumMultiplier: Multiplier = Multiplier::one();
- pub MaximumMultiplier: Multiplier = Bounded::max_value();
+ // This value increases the priority of `Operational` transactions by adding
+ // a "virtual tip" that's equal to the `OperationalFeeMultiplier * final_fee`.
+ // follows polkadot : https://github.com/paritytech/polkadot/blob/9ce5f7ef5abb1a4291454e8c9911b304d80679f9/runtime/polkadot/src/lib.rs#L369
+ pub const OperationalFeeMultiplier: u8 = 5;
+ // We expect that on average 25% of the normal capacity will be occupied with normal txs.
+ pub const TargetSaturationLevel: Perquintill = Perquintill::from_percent(25);
+ // During 20 blocks the fee may not change more than by 100%. This, together with the
+ // `TargetSaturationLevel` value, results in variability ~0.067. For the corresponding
+ // formulas please refer to Substrate code at `frame/transaction-payment/src/lib.rs`.
+ pub FeeVariability: Multiplier = Multiplier::saturating_from_rational(67, 1000);
+ // Fee should never be lower than the computational cost.
+ pub MinimumMultiplier: Multiplier = Multiplier::one();
+ pub MaximumMultiplier: Multiplier = Bounded::max_value();
}
parameter_types! {
- pub FeeMultiplier: Multiplier = Multiplier::one();
+ pub FeeMultiplier: Multiplier = Multiplier::one();
}
impl pallet_transaction_payment::Config for Runtime {
- type RuntimeEvent = RuntimeEvent;
- type OnChargeTransaction = FungibleAdapter<Balances, ()>;
- type OperationalFeeMultiplier = ConstU8<5>;
- type WeightToFee = IdentityFee<Balance>;
- type LengthToFee = IdentityFee<Balance>;
- type FeeMultiplierUpdate = ConstFeeMultiplier<FeeMultiplier>;
+ type RuntimeEvent = RuntimeEvent;
+ type OnChargeTransaction = FungibleAdapter<Balances, ()>;
+ type OperationalFeeMultiplier = ConstU8<5>;
+ type WeightToFee = IdentityFee<Balance>;
+ type LengthToFee = IdentityFee<Balance>;
+ type FeeMultiplierUpdate = ConstFeeMultiplier<FeeMultiplier>;
}
construct_runtime!(
- pub struct Runtime {
- System: frame_system,
- Timestamp: pallet_timestamp,
- Balances: pallet_balances,
- TransactionPayment: pallet_transaction_payment,
- Md5DifficultyAdjustment: difficulty::<Instance1>,
- Sha3DifficultyAdjustment: difficulty::<Instance2>,
- KeccakDifficultyAdjustment: difficulty::<Instance3>,
- BlockAuthor: block_author,
- }
+ pub struct Runtime {
+ System: frame_system,
+ Timestamp: pallet_timestamp,
+ Balances: pallet_balances,
+ TransactionPayment: pallet_transaction_payment,
+ Md5DifficultyAdjustment: difficulty::<Instance1>,
+ Sha3DifficultyAdjustment: difficulty::<Instance2>,
+ KeccakDifficultyAdjustment: difficulty::<Instance3>,
+ BlockAuthor: block_author,
+ }
);
/// The address format for describing accounts.
@@ -353,177 +348,177 @@ pub type Block = generic::Block<Header, UncheckedExtrinsic>;
pub type SignedBlock = generic::SignedBlock<Block>;
/// The SignedExtension to the basic transaction logic.
pub type SignedExtra = (
- frame_system::CheckNonZeroSender<Runtime>,
- frame_system::CheckSpecVersion<Runtime>,
- frame_system::CheckTxVersion<Runtime>,
- frame_system::CheckGenesis<Runtime>,
- frame_system::CheckEra<Runtime>,
- frame_system::CheckNonce<Runtime>,
- frame_system::CheckWeight<Runtime>,
- pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
+ frame_system::CheckNonZeroSender<Runtime>,
+ frame_system::CheckSpecVersion<Runtime>,
+ frame_system::CheckTxVersion<Runtime>,
+ frame_system::CheckGenesis<Runtime>,
+ frame_system::CheckEra<Runtime>,
+ frame_system::CheckNonce<Runtime>,
+ frame_system::CheckWeight<Runtime>,
+ pallet_transaction_payment::ChargeTransactionPayment<Runtime>,
);
/// Unchecked extrinsic type as expected by this runtime.
pub type UncheckedExtrinsic =
- generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>;
+ generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>;
/// Executive: handles dispatch to the various modules.
pub type Executive = frame_executive::Executive<
- Runtime,
- Block,
- frame_system::ChainContext<Runtime>,
- Runtime,
- AllPalletsWithSystem,
+ Runtime,
+ Block,
+ frame_system::ChainContext<Runtime>,
+ Runtime,
+ AllPalletsWithSystem,
>;
impl_runtime_apis! {
- impl sp_api::Core<Block> for Runtime {
- fn version() -> RuntimeVersion {
- VERSION
- }
-
- fn execute_block(block: Block) {
- Executive::execute_block(block)
- }
-
- fn initialize_block(header: &<Block as BlockT>::Header) -> ExtrinsicInclusionMode {
- Executive::initialize_block(header)
- }
- }
-
- impl sp_api::Metadata<Block> for Runtime {
- fn metadata() -> OpaqueMetadata {
- OpaqueMetadata::new(Runtime::metadata().into())
- }
-
- fn metadata_at_version(version: u32) -> Option<OpaqueMetadata> {
- Runtime::metadata_at_version(version)
- }
-
- fn metadata_versions() -> sp_std::vec::Vec<u32> {
- Runtime::metadata_versions()
- }
- }
-
- impl sp_block_builder::BlockBuilder<Block> for Runtime {
- fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {
- Executive::apply_extrinsic(extrinsic)
- }
-
- fn finalize_block() -> <Block as BlockT>::Header {
- Executive::finalize_block()
- }
-
- fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {
- data.create_extrinsics()
- }
-
- fn check_inherents(
- block: Block,
- data: sp_inherents::InherentData,
- ) -> sp_inherents::CheckInherentsResult {
- data.check_extrinsics(&block)
- }
- }
-
- impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {
- fn validate_transaction(
- source: TransactionSource,
- tx: <Block as BlockT>::Extrinsic,
- block_hash: <Block as BlockT>::Hash,
- ) -> TransactionValidity {
- Executive::validate_transaction(source, tx, block_hash)
- }
- }
-
- impl sp_offchain::OffchainWorkerApi<Block> for Runtime {
- fn offchain_worker(header: &<Block as BlockT>::Header) {
- Executive::offchain_worker(header)
- }
- }
-
- impl sp_session::SessionKeys<Block> for Runtime {
- fn generate_session_keys(_seed: Option<Vec<u8>>) -> Vec<u8> {
- Vec::new()
- }
-
- fn decode_session_keys(
- _encoded: Vec<u8>,
- ) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
- None
- }
- }
-
- impl sp_consensus_pow::DifficultyApi<Block, multi_pow::Threshold> for Runtime {
- fn difficulty() -> multi_pow::Threshold {
- multi_pow::Threshold {
- md5: Md5DifficultyAdjustment::difficulty(),
- sha3: Sha3DifficultyAdjustment::difficulty(),
- keccak: KeccakDifficultyAdjustment::difficulty(),
- }
- }
- }
-
- impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {
- fn account_nonce(account: AccountId) -> Index {
- System::account_nonce(account)
- }
- }
-
- impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime
- {
- fn query_info(
- uxt: <Block as BlockT>::Extrinsic,
- len: u32,
- ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> {
- TransactionPayment::query_info(uxt, len)
- }
- fn query_fee_details(
- uxt: <Block as BlockT>::Extrinsic,
- len: u32,
- ) -> pallet_transaction_payment::FeeDetails<Balance> {
- TransactionPayment::query_fee_details(uxt, len)
- }
- fn query_weight_to_fee(weight: Weight) -> Balance {
- TransactionPayment::weight_to_fee(weight)
- }
- fn query_length_to_fee(length: u32) -> Balance {
- TransactionPayment::length_to_fee(length)
- }
- }
-
- impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi<Block, Balance, RuntimeCall>
- for Runtime
- {
- fn query_call_info(
- call: RuntimeCall,
- len: u32,
- ) -> pallet_transaction_payment::RuntimeDispatchInfo<Balance> {
- TransactionPayment::query_call_info(call, len)
- }
- fn query_call_fee_details(
- call: RuntimeCall,
- len: u32,
- ) -> pallet_transaction_payment::FeeDetails<Balance> {
- TransactionPayment::query_call_fee_details(call, len)
- }
- fn query_weight_to_fee(weight: Weight) -> Balance {
- TransactionPayment::weight_to_fee(weight)
- }
- fn query_length_to_fee(length: u32) -> Balance {
- TransactionPayment::length_to_fee(length)
- }
- }
-
- impl sp_genesis_builder::GenesisBuilder<Block> for Runtime {
- fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result {
- build_state::<RuntimeGenesisConfig>(config)
- }
-
- fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
- get_preset::<RuntimeGenesisConfig>(id, |_| None)
- }
-
- fn preset_names() -> Vec<sp_genesis_builder::PresetId> {
- Default::default()
- }
- }
+ impl sp_api::Core<Block> for Runtime {
+ fn version() -> RuntimeVersion {
+ VERSION
+ }
+
+ fn execute_block(block: Block) {
+ Executive::execute_block(block)
+ }
+
+ fn initialize_block(header: &<Block as BlockT>::Header) -> ExtrinsicInclusionMode {
+ Executive::initialize_block(header)
+ }
+ }
+
+ impl sp_api::Metadata<Block> for Runtime {
+ fn metadata() -> OpaqueMetadata {
+ OpaqueMetadata::new(Runtime::metadata().into())
+ }
+
+ fn metadata_at_version(version: u32) -> Option<OpaqueMetadata> {
+ Runtime::metadata_at_version(version)
+ }
+
+ fn metadata_versions() -> sp_std::vec::Vec<u32> {
+ Runtime::metadata_versions()
+ }
+ }
+
+ impl sp_block_builder::BlockBuilder<Block> for Runtime {
+ fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult {
+ Executive::apply_extrinsic(extrinsic)
+ }
+
+ fn finalize_block() -> <Block as BlockT>::Header {
+ Executive::finalize_block()
+ }
+
+ fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> {
+ data.create_extrinsics()
+ }
+
+ fn check_inherents(
+ block: Block,
+ data: sp_inherents::InherentData,
+ ) -> sp_inherents::CheckInherentsResult {
+ data.check_extrinsics(&block)
+ }
+ }
+
+ impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {
+ fn validate_transaction(
+ source: TransactionSource,
+ tx: <Block as BlockT>::Extrinsic,
+ block_hash: <Block as BlockT>::Hash,
+ ) -> TransactionValidity {
+ Executive::validate_transaction(source, tx, block_hash)
+ }
+ }
+
+ impl sp_offchain::OffchainWorkerApi<Block> for Runtime {
+ fn offchain_worker(header: &<Block as BlockT>::Header) {
+ Executive::offchain_worker(header)
+ }
+ }
+
+ impl sp_session::SessionKeys<Block> for Runtime {
+ fn generate_session_keys(_seed: Option<Vec<u8>>) -> Vec<u8> {
+ Vec::new()
+ }
+
+ fn decode_session_keys(
+ _encoded: Vec<u8>,
+ ) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> {
+ None
+ }
+ }
+
+ impl sp_consensus_pow::DifficultyApi<Block, multi_pow::Threshold> for Runtime {
+ fn difficulty() -> multi_pow::Threshold {
+ multi_pow::Threshold {
+ md5: Md5DifficultyAdjustment::difficulty(),
+ sha3: Sha3DifficultyAdjustment::difficulty(),
+ keccak: KeccakDifficultyAdjustment::difficulty(),
+ }
+ }
+ }
+
+ impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime {
+ fn account_nonce(account: AccountId) -> Index {
+ System::account_nonce(account)
+ }
+ }
+
+ impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime
+ {
+ fn query_info(
+ uxt: <Block as BlockT>::Extrinsic,
+ len: u32,
+ ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> {
+ TransactionPayment::query_info(uxt, len)
+ }
+ fn query_fee_details(
+ uxt: <Block as BlockT>::Extrinsic,
+ len: u32,
+ ) -> pallet_transaction_payment::FeeDetails<Balance> {
+ TransactionPayment::query_fee_details(uxt, len)
+ }
+ fn query_weight_to_fee(weight: Weight) -> Balance {
+ TransactionPayment::weight_to_fee(weight)
+ }
+ fn query_length_to_fee(length: u32) -> Balance {
+ TransactionPayment::length_to_fee(length)
+ }
+ }
+
+ impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi<Block, Balance, RuntimeCall>
+ for Runtime
+ {
+ fn query_call_info(
+ call: RuntimeCall,
+ len: u32,
+ ) -> pallet_transaction_payment::RuntimeDispatchInfo<Balance> {
+ TransactionPayment::query_call_info(call, len)
+ }
+ fn query_call_fee_details(
+ call: RuntimeCall,
+ len: u32,
+ ) -> pallet_transaction_payment::FeeDetails<Balance> {
+ TransactionPayment::query_call_fee_details(call, len)
+ }
+ fn query_weight_to_fee(weight: Weight) -> Balance {
+ TransactionPayment::weight_to_fee(weight)
+ }
+ fn query_length_to_fee(length: u32) -> Balance {
+ TransactionPayment::length_to_fee(length)
+ }
+ }
+
+ impl sp_genesis_builder::GenesisBuilder<Block> for Runtime {
+ fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result {
+ build_state::<RuntimeGenesisConfig>(config)
+ }
+
+ fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> {
+ get_preset::<RuntimeGenesisConfig>(id, |_| None)
+ }
+
+ fn preset_names() -> Vec<sp_genesis_builder::PresetId> {
+ Default::default()
+ }
+ }
}
diff --git a/runtime/src/utxo.rs b/runtime/src/utxo.rs
index d51c203..d387d16 100644
--- a/runtime/src/utxo.rs
+++ b/runtime/src/utxo.rs
@@ -6,8 +6,8 @@ use scale_info::TypeInfo;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sp_core::{
- sr25519::{Public, Signature},
- ByteArray, H256, H512,
+ sr25519::{Public, Signature},
+ ByteArray, H256, H512,
};
use sp_runtime::traits::{BlakeTwo256, Hash, SaturatedConversion};
use sp_std::{collections::btree_map::BTreeMap, vec::Vec};
@@ -18,14 +18,14 @@ pub use pallet::*;
#[frame_support::pallet(dev_mode)]
pub mod pallet {
- use frame_support::pallet_prelude::*;
- use frame_system::pallet_prelude::*;
+ use frame_support::pallet_prelude::*;
+ use frame_system::pallet_prelude::*;
- use super::*;
+ use super::*;
- #[pallet::config]
- pub trait Config: frame_system::Config { }
+ #[pallet::config]
+ pub trait Config: frame_system::Config {}
- #[pallet::pallet]
- pub struct Pallet<T>(_);
+ #[pallet::pallet]
+ pub struct Pallet<T>(_);
}
diff --git a/rustfmt.toml b/rustfmt.toml
index af85000..c342153 100644
--- a/rustfmt.toml
+++ b/rustfmt.toml
@@ -1,7 +1,24 @@
+# Basic
edition = "2021"
-use_field_init_shorthand = true
-reorder_modules = true
-
+hard_tabs = true
+max_width = 100
+use_small_heuristics = "Max"
+# Imports
imports_granularity = "Crate"
-group_imports = "StdExternalCrate"
reorder_imports = true
+# Consistency
+newline_style = "Unix"
+# 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
+# Format comments
+comment_width = 100
+wrap_comments = true
The UTXO Pallet
In this section, we will build the UTXO Pallet and apply it to the PoW Runtime & Node
This Pallet will manage the balances of users and allow them to transfer tokens to one another instead of Account-based model.
Reading materials:
Creating UTXO Pallet
[!Note] Resolve all "/// TODO" in
runtime/src/utxo.rs
to complete this step.
Reading Materials
I would recommend you to read these materials below first before looking at the code implmentation of the data structures. These materials below cover very well the concepts of FRAME storage in Substrate development.
- Substrate Tutorial - Use macros in a custom pallet
- OpenGuild Blog - Code breakdown pallet template (Vietnamese)
- Polkadot Blockchain Academy - FRAME Storage lecture
- Substrate Docs - Runtime storage structure
- Polkadot Blockchain Academy - Event and Error
- Polkadot Blockchain Academy - Pallet coupling
Data structures to work with Storage API
The FRAME Storage module simplifies access to these layered storage abstractions. You can use the FRAME storage data structures to read or write any value that can be encoded by the SCALE codec. The storage module provides the following types of storage structures:
StorageValue
: to store any single value, such as a u64.StorageMap
: to store a single key to value mapping, such as a specific account key to a specific balance value.StorageDoubleMap
: to store values in a storage map with two keys as an optimization to efficiently remove all entries that have a common first key.StorageNMap
: to store values in a map with any arbitrary number of keys.
Struct data for UTXO
Simple type for storing balance of UTXO
#![allow(unused)] fn main() { pub type Value = u128; }
Struct for representing a transaction in a UTXO-based model. The struct includes macros for serialization, deserialization, and various traits that enable efficient use in a blockchain context. Let’s break down the purpose of each macro:
#![allow(unused)] fn main() { #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct Transaction { /// UTXOs to be used as inputs for current transaction pub inputs: Vec<TransactionInput>, /// UTXOs to be created as a result of current transaction dispatch pub outputs: Vec<TransactionOutput>, } #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct TransactionInput { /// Reference to an UTXO to be spent pub outpoint: H256, /// Proof that transaction owner is authorized to spend referred UTXO & /// that the entire transaction is untampered pub sigscript: H512, } #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct TransactionOutput { /// Value associated with this output pub value: Value, /// Public key associated with this output. In order to spend this output /// owner must provide a proof by hashing the whole `Transaction` and /// signing it with a corresponding private key. pub pubkey: H256, } }
-
cfg_attr(feature = "std", derive(Serialize, Deserialize))
: This macro conditionally derives theSerialize
andDeserialize
traits when thestd
feature is enabled. This feature is useful for converting theTransaction
struct to and from formats like JSON or other text-based formats. This functionality is often used in scenarios like data exchange between systems, debugging, or interacting with APIs or frontends. -
Encode
,Decode
: These macros from the parity-scale-codec crate allow the struct to be serialized to and deserialized from the SCALE binary format, ensuring efficient storage and transmission on the blockchain. Read more SCALE. -
TypeInfo
: Generates metadata for the struct, allowing its type information to be included in the blockchain's runtime metadata. This is valuable for interoperability with tools like Substrate frontends.
Configure your pallet's types, events and errors
#![allow(unused)] fn main() { #[pallet::config] pub trait Config: frame_system::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. /// Read more: https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_runtime_types/index.html type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; /// A source to determine the block author /// Read more: `runtime/src/block_author.rs` /// Pallet loosely coupling /// https://polkadot-blockchain-academy.github.io/pba-book/frame/coupling/page.html#loosely-coupled-pallets type BlockAuthor: BlockAuthor; /// A source to determine the issuance portion of the block reward /// Read more: `runtime/src/issuance.rs` type Issuance: Issuance<BlockNumberFor<Self>, Value>; } }
This storage item represents the total reward value to be redistributed among authorities during block finalization.
#![allow(unused)] fn main() { /// Total reward value to be redistributed among authorities. /// It is accumulated from transactions during block execution /// and then dispersed to validators on block finalization. #[pallet::storage] #[pallet::getter(fn total_reward)] pub type TotalReward<T: Config> = StorageValue<_, Value, ValueQuery>; }
This storage item represents a mapping of UTXOs to their respective keys, allowing efficient lookup and management of UTXOs in a UTXO-based blockchain model.
#![allow(unused)] fn main() { /// All valid unspent transaction outputs are stored in this map. /// Initial set of UTXO is populated from the list stored in genesis. /// We use the identity hasher here because the cryptographic hashing is /// done explicitly. /// Mapping from `BlakeTwo256::hash_of(transaction, index)` to `TransactionOutput` #[pallet::storage] #[pallet::getter(fn utxo_store)] pub type UtxoStore<T: Config> = StorageMap< Hasher = Identity, Key = H256, Value = TransactionOutput, QueryKind = OptionQuery, >; }
Events & Errors
Events and errors are used to notify about specific activity. Please use this for debugging purpose only. Events and Errors should not be used as a communication method between functionalities. In our codebase, we will declare these errors and events.
To declare error, simply use macro #[pallet::error]
#![allow(unused)] fn main() { /// Errors inform users that something went wrong. #[pallet::error] pub enum Error<T> { /// Missing `Transaction` Input MissingInput, /// Reward overflow RewardOverflow, /// Maximum transaction depth MaximumTransactionDepth, /// Empty input EmptyInput, /// Empty output EmptyOutput, /// Each input must only be used once DuplicatedInput, /// Each output must be defined only once DuplicatedOutput, /// Input value is overflow InputOverflow, /// Output value is overflow OutputOverflow, /// Output spent must lte than Input spent OutputOverInput, /// Zero amount spent ZeroAmount, /// Invalid signature InvalidSignature, } }
To declare event, use #[pallet::event]
. Moreover with #[pallet::generate_deposit(pub(super) fn deposit_event)]
, it automatically generate a function deposit_event
for emitting events.
#![allow(unused)] fn main() { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config> { /// Dispatch transaction successful TransactionSuccess(Transaction), /// UTXO out processed TransactionOutputProcessed(H256), /// Reward distributed to `BlockAuthor` RewardDistributed(Value, H256), /// Faucet to `To` Faucet(Value, H256), /// No one get reward RewardWasted, } }
Build the code
cargo build --release
Great job! 🎉 You're making fantastic progress. Let's keep the momentum going and dive into next step
#![allow(unused)] fn main() { // We make sure this pallet uses `no_std` for compiling to Wasm. #![cfg_attr(not(feature = "std"), no_std)] use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_core::{ sr25519::{Public, Signature}, ByteArray, H256, H512, }; use sp_runtime::traits::{BlakeTwo256, Hash, SaturatedConversion}; use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; use super::{block_author::BlockAuthor, issuance::Issuance}; pub use pallet::*; /// TODO [2-data-structure] // pub type Value = u128; /// TODO [2-data-structure] /// Single transaction to be dispatched // #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] // #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] // pub struct Transaction { // /// UTXOs to be used as inputs for current transaction // pub inputs: Vec<TransactionInput>, // /// UTXOs to be created as a result of current transaction dispatch // pub outputs: Vec<TransactionOutput>, // } /// TODO [2-data-structure] /// Single transaction input that refers to one UTXO // #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] // #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] // pub struct TransactionInput { // /// Reference to an UTXO to be spent // pub outpoint: H256, // /// Proof that transaction owner is authorized to spend referred UTXO & // /// that the entire transaction is untampered // pub sigscript: H512, // } /// TODO [2-data-structure] /// Single transaction output to create upon transaction dispatch // #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] // #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] // pub struct TransactionOutput { // /// Value associated with this output // pub value: Value, // /// Public key associated with this output. In order to spend this output // /// owner must provide a proof by hashing the whole `Transaction` and // /// signing it with a corresponding private key. // pub pubkey: H256, // } #[frame_support::pallet(dev_mode)] pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use super::*; /// TODO [2-data-structure] #[pallet::config] pub trait Config: frame_system::Config { // /// Because this pallet emits events, it depends on the runtime's definition of an event. // /// Read more: https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_runtime_types/index.html // type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; // /// A source to determine the block author // /// Read more: `runtime/src/block_author.rs` // /// Pallet loosely coupling // /// https://polkadot-blockchain-academy.github.io/pba-book/frame/coupling/page.html#loosely-coupled-pallets // type BlockAuthor: BlockAuthor; // /// A source to determine the issuance portion of the block reward // type Issuance: Issuance<BlockNumberFor<Self>, Value>; } #[pallet::pallet] pub struct Pallet<T>(_); /// TODO [2-data-structure] /// Total reward value to be redistributed among authorities. /// It is accumulated from transactions during block execution /// and then dispersed to validators on block finalization. // #[pallet::storage] // #[pallet::getter(fn total_reward)] // pub type TotalReward<T: Config> = StorageValue<_, Value, ValueQuery>; /// TODO [2-data-structure] /// All valid unspent transaction outputs are stored in this map. /// Initial set of UTXO is populated from the list stored in genesis. /// We use the identity hasher here because the cryptographic hashing is /// done explicitly. /// Mapping from `BlakeTwo256::hash_of(transaction, index)` to `TransactionOutput` // #[pallet::storage] // #[pallet::getter(fn utxo_store)] // pub type UtxoStore<T: Config> = StorageMap< // Hasher = Identity, // Key = H256, // Value = TransactionOutput, // QueryKind = OptionQuery, // >; /// TODO [2-data-structure] /// Pallets use events to inform users when important changes are made. // #[pallet::event] // #[pallet::generate_deposit(pub(super) fn deposit_event)] // pub enum Event<T: Config> { // /// Dispatch transaction successful // TransactionSuccess(Transaction), // /// UTXO out processed // TransactionOutputProcessed(H256), // /// Reward distributed to `BlockAuthor` // RewardDistributed(Value, H256), // /// Faucet to `To` // Faucet(Value, H256), // /// No one get reward // RewardWasted, // } /// TODO [2-data-structure] /// Errors inform users that something went wrong. // #[pallet::error] // pub enum Error<T> { // /// Missing `Transaction` Input // MissingInput, // /// Reward overflow // RewardOverflow, // /// Maximum transaction depth // MaximumTransactionDepth, // /// Empty input // EmptyInput, // /// Empty output // EmptyOutput, // /// Each input must only be used once // DuplicatedInput, // /// Each output must be defined only once // DuplicatedOutput, // /// Input value is overflow // InputOverflow, // /// Output value is overflow // OutputOverflow, // /// Output spent must lte than Input spent // OutputOverInput, // /// Zero amount spent // ZeroAmount, // /// Invalid signature // InvalidSignature, // } /// TODO [2-data-structure] /// Define extrinsics / dispatchable function // #[pallet::call] // impl<T: Config> Pallet<T> { // pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult { // todo!(); // } // } /// TODO [2-data-structure] /// Define intrinsics // impl<T: Config> Pallet<T> { // /// Implement spend logic, update storage to reflect changes made by transaction // /// Where each UTXO key is a hash of the entire transaction and its order in the `TransactionOutputs` vector // fn do_spend(transaction: &Transaction, reward: Value) -> DispatchResult { // todo!(); // } // /// Redistribute combined reward value to block Author // fn disperse_reward(author: &Public) { // todo!(); // } // /// Mutate storage, insert / update new UTXOs // fn store_utxo(utxo: &TransactionOutput, hash: H256) { // todo!(); // } // /// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash. // fn get_simple_transaction(transaction: &Transaction) -> Vec<u8> { // todo!(); // } // /// Check transaction for validity, errors, & race conditions // /// Called by both transaction pool and runtime execution // pub fn validate_transaction( // transaction: &Transaction, // ) -> Result<ValidTransaction, &'static str> { // todo!(); // } // } } }
#![allow(unused)] fn main() { // We make sure this pallet uses `no_std` for compiling to Wasm. #![cfg_attr(not(feature = "std"), no_std)] use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_core::{ sr25519::{Public, Signature}, ByteArray, H256, H512, }; use sp_runtime::traits::{BlakeTwo256, Hash, SaturatedConversion}; use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; use super::{block_author::BlockAuthor, issuance::Issuance}; pub use pallet::*; /// [2-data-structure] pub type Value = u128; /// [2-data-structure] /// Single transaction to be dispatched #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct Transaction { /// UTXOs to be used as inputs for current transaction pub inputs: Vec<TransactionInput>, /// UTXOs to be created as a result of current transaction dispatch pub outputs: Vec<TransactionOutput>, } /// [2-data-structure] /// Single transaction input that refers to one UTXO #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct TransactionInput { /// Reference to an UTXO to be spent pub outpoint: H256, /// Proof that transaction owner is authorized to spend referred UTXO & /// that the entire transaction is untampered pub sigscript: H512, } /// [2-data-structure] /// Single transaction output to create upon transaction dispatch #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct TransactionOutput { /// Value associated with this output pub value: Value, /// Public key associated with this output. In order to spend this output /// owner must provide a proof by hashing the whole `Transaction` and /// signing it with a corresponding private key. pub pubkey: H256, } #[frame_support::pallet(dev_mode)] pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use super::*; /// [2-data-structure] #[pallet::config] pub trait Config: frame_system::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. /// Read more: https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_runtime_types/index.html type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; /// A source to determine the block author /// Read more: `runtime/src/block_author.rs` /// Pallet loosely coupling /// https://polkadot-blockchain-academy.github.io/pba-book/frame/coupling/page.html#loosely-coupled-pallets type BlockAuthor: BlockAuthor; /// A source to determine the issuance portion of the block reward type Issuance: Issuance<BlockNumberFor<Self>, Value>; } #[pallet::pallet] pub struct Pallet<T>(_); /// [2-data-structure] /// Total reward value to be redistributed among authorities. /// It is accumulated from transactions during block execution /// and then dispersed to validators on block finalization. #[pallet::storage] #[pallet::getter(fn total_reward)] pub type TotalReward<T: Config> = StorageValue<_, Value, ValueQuery>; /// [2-data-structure] /// All valid unspent transaction outputs are stored in this map. /// Initial set of UTXO is populated from the list stored in genesis. /// We use the identity hasher here because the cryptographic hashing is /// done explicitly. /// Mapping from `BlakeTwo256::hash_of(transaction, index)` to `TransactionOutput` #[pallet::storage] #[pallet::getter(fn utxo_store)] pub type UtxoStore<T: Config> = StorageMap< Hasher = Identity, Key = H256, Value = TransactionOutput, QueryKind = OptionQuery, >; /// [2-data-structure] /// Pallets use events to inform users when important changes are made. #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config> { /// Dispatch transaction successful TransactionSuccess(Transaction), /// UTXO out processed TransactionOutputProcessed(H256), /// Reward distributed to `BlockAuthor` RewardDistributed(Value, H256), /// Faucet to `To` Faucet(Value, H256), /// No one get reward RewardWasted, } /// [2-data-structure] /// Errors inform users that something went wrong. #[pallet::error] pub enum Error<T> { /// Missing `Transaction` Input MissingInput, /// Reward overflow RewardOverflow, /// Maximum transaction depth MaximumTransactionDepth, /// Empty input EmptyInput, /// Empty output EmptyOutput, /// Each input must only be used once DuplicatedInput, /// Each output must be defined only once DuplicatedOutput, /// Input value is overflow InputOverflow, /// Output value is overflow OutputOverflow, /// Output spent must lte than Input spent OutputOverInput, /// Zero amount spent ZeroAmount, /// Invalid signature InvalidSignature, } /// [2-data-structure] /// Define extrinsics / dispatchable function #[pallet::call] impl<T: Config> Pallet<T> { pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult { todo!(); } } /// [2-data-structure] /// Define intrinsics impl<T: Config> Pallet<T> { /// Implement spend logic, update storage to reflect changes made by transaction /// Where each UTXO key is a hash of the entire transaction and its order in the `TransactionOutputs` vector fn do_spend(transaction: &Transaction, reward: Value) -> DispatchResult { todo!(); } /// Redistribute combined reward value to block Author fn disperse_reward(author: &Public) { todo!(); } /// Mutate storage, insert / update new UTXOs fn store_utxo(utxo: &TransactionOutput, hash: H256) { todo!(); } /// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash. fn get_simple_transaction(transaction: &Transaction) -> Vec<u8> { todo!(); } /// Check transaction for validity, errors, & race conditions /// Called by both transaction pool and runtime execution pub fn validate_transaction( transaction: &Transaction, ) -> Result<ValidTransaction, &'static str> { todo!(); } } } }
diff --git a/runtime/src/utxo.rs b/runtime/src/utxo.rs
index d387d16..47950e4 100644
--- a/runtime/src/utxo.rs
+++ b/runtime/src/utxo.rs
@@ -6,8 +6,8 @@ use scale_info::TypeInfo;
#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sp_core::{
- sr25519::{Public, Signature},
- ByteArray, H256, H512,
+ sr25519::{Public, Signature},
+ ByteArray, H256, H512,
};
use sp_runtime::traits::{BlakeTwo256, Hash, SaturatedConversion};
use sp_std::{collections::btree_map::BTreeMap, vec::Vec};
@@ -16,16 +16,188 @@ use super::{block_author::BlockAuthor, issuance::Issuance};
pub use pallet::*;
+/// TODO [2-data-structure]
+// pub type Value = u128;
+
+/// TODO [2-data-structure]
+/// Single transaction to be dispatched
+// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+// #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
+// pub struct Transaction {
+// /// UTXOs to be used as inputs for current transaction
+// pub inputs: Vec<TransactionInput>,
+// /// UTXOs to be created as a result of current transaction dispatch
+// pub outputs: Vec<TransactionOutput>,
+// }
+
+/// TODO [2-data-structure]
+/// Single transaction input that refers to one UTXO
+// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+// #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
+// pub struct TransactionInput {
+// /// Reference to an UTXO to be spent
+// pub outpoint: H256,
+
+// /// Proof that transaction owner is authorized to spend referred UTXO &
+// /// that the entire transaction is untampered
+// pub sigscript: H512,
+// }
+
+/// TODO [2-data-structure]
+/// Single transaction output to create upon transaction dispatch
+// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+// #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
+// pub struct TransactionOutput {
+// /// Value associated with this output
+// pub value: Value,
+
+// /// Public key associated with this output. In order to spend this output
+// /// owner must provide a proof by hashing the whole `Transaction` and
+// /// signing it with a corresponding private key.
+// pub pubkey: H256,
+// }
+
#[frame_support::pallet(dev_mode)]
pub mod pallet {
- use frame_support::pallet_prelude::*;
- use frame_system::pallet_prelude::*;
+ use frame_support::pallet_prelude::*;
+ use frame_system::pallet_prelude::*;
+
+ use super::*;
+
+ /// TODO [2-data-structure]
+ #[pallet::config]
+ pub trait Config: frame_system::Config {
+ // /// Because this pallet emits events, it depends on the runtime's definition of an event.
+ // /// Read more: https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_runtime_types/index.html
+ // type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
+
+ // /// A source to determine the block author
+ // /// Read more: `runtime/src/block_author.rs`
+ // /// Pallet loosely coupling
+ // /// https://polkadot-blockchain-academy.github.io/pba-book/frame/coupling/page.html#loosely-coupled-pallets
+ // type BlockAuthor: BlockAuthor;
+
+ // /// A source to determine the issuance portion of the block reward
+ // type Issuance: Issuance<BlockNumberFor<Self>, Value>;
+ }
+
+ #[pallet::pallet]
+ pub struct Pallet<T>(_);
+
+
+ /// TODO [2-data-structure]
+ /// Total reward value to be redistributed among authorities.
+ /// It is accumulated from transactions during block execution
+ /// and then dispersed to validators on block finalization.
+ // #[pallet::storage]
+ // #[pallet::getter(fn total_reward)]
+ // pub type TotalReward<T: Config> = StorageValue<_, Value, ValueQuery>;
+
+ /// TODO [2-data-structure]
+ /// All valid unspent transaction outputs are stored in this map.
+ /// Initial set of UTXO is populated from the list stored in genesis.
+ /// We use the identity hasher here because the cryptographic hashing is
+ /// done explicitly.
+ /// Mapping from `BlakeTwo256::hash_of(transaction, index)` to `TransactionOutput`
+ // #[pallet::storage]
+ // #[pallet::getter(fn utxo_store)]
+ // pub type UtxoStore<T: Config> = StorageMap<
+ // Hasher = Identity,
+ // Key = H256,
+ // Value = TransactionOutput,
+ // QueryKind = OptionQuery,
+ // >;
+
+
+ /// TODO [2-data-structure]
+ /// Pallets use events to inform users when important changes are made.
+ // #[pallet::event]
+ // #[pallet::generate_deposit(pub(super) fn deposit_event)]
+ // pub enum Event<T: Config> {
+ // /// Dispatch transaction successful
+ // TransactionSuccess(Transaction),
+ // /// UTXO out processed
+ // TransactionOutputProcessed(H256),
+ // /// Reward distributed to `BlockAuthor`
+ // RewardDistributed(Value, H256),
+ // /// Faucet to `To`
+ // Faucet(Value, H256),
+ // /// No one get reward
+ // RewardWasted,
+ // }
+
+
+ /// TODO [2-data-structure]
+ /// Errors inform users that something went wrong.
+ // #[pallet::error]
+ // pub enum Error<T> {
+ // /// Missing `Transaction` Input
+ // MissingInput,
+ // /// Reward overflow
+ // RewardOverflow,
+ // /// Maximum transaction depth
+ // MaximumTransactionDepth,
+ // /// Empty input
+ // EmptyInput,
+ // /// Empty output
+ // EmptyOutput,
+ // /// Each input must only be used once
+ // DuplicatedInput,
+ // /// Each output must be defined only once
+ // DuplicatedOutput,
+ // /// Input value is overflow
+ // InputOverflow,
+ // /// Output value is overflow
+ // OutputOverflow,
+ // /// Output spent must lte than Input spent
+ // OutputOverInput,
+ // /// Zero amount spent
+ // ZeroAmount,
+ // /// Invalid signature
+ // InvalidSignature,
+ // }
+
+
+ /// TODO [2-data-structure]
+ /// Define extrinsics / dispatchable function
+ // #[pallet::call]
+ // impl<T: Config> Pallet<T> {
+ // pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult {
+ // todo!();
+ // }
+
+ // }
+
+ /// TODO [2-data-structure]
+ /// Define intrinsics
+ // impl<T: Config> Pallet<T> {
+ // /// Implement spend logic, update storage to reflect changes made by transaction
+ // /// Where each UTXO key is a hash of the entire transaction and its order in the `TransactionOutputs` vector
+ // fn do_spend(transaction: &Transaction, reward: Value) -> DispatchResult {
+ // todo!();
+ // }
+
+ // /// Redistribute combined reward value to block Author
+ // fn disperse_reward(author: &Public) {
+ // todo!();
+ // }
- use super::*;
+ // /// Mutate storage, insert / update new UTXOs
+ // fn store_utxo(utxo: &TransactionOutput, hash: H256) {
+ // todo!();
+ // }
- #[pallet::config]
- pub trait Config: frame_system::Config {}
+ // /// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash.
+ // fn get_simple_transaction(transaction: &Transaction) -> Vec<u8> {
+ // todo!();
+ // }
- #[pallet::pallet]
- pub struct Pallet<T>(_);
-}
+ // /// Check transaction for validity, errors, & race conditions
+ // /// Called by both transaction pool and runtime execution
+ // pub fn validate_transaction(
+ // transaction: &Transaction,
+ // ) -> Result<ValidTransaction, &'static str> {
+ // todo!();
+ // }
+ // }
+}
\ No newline at end of file
diff --git a/runtime/src/utxo.rs b/runtime/src/utxo.rs
index 47950e4..69a9535 100644
--- a/runtime/src/utxo.rs
+++ b/runtime/src/utxo.rs
@@ -16,46 +16,46 @@ use super::{block_author::BlockAuthor, issuance::Issuance};
pub use pallet::*;
-/// TODO [2-data-structure]
-// pub type Value = u128;
+/// [2-data-structure]
+pub type Value = u128;
-/// TODO [2-data-structure]
+/// [2-data-structure]
/// Single transaction to be dispatched
-// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-// #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
-// pub struct Transaction {
-// /// UTXOs to be used as inputs for current transaction
-// pub inputs: Vec<TransactionInput>,
-// /// UTXOs to be created as a result of current transaction dispatch
-// pub outputs: Vec<TransactionOutput>,
-// }
-
-/// TODO [2-data-structure]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
+pub struct Transaction {
+ /// UTXOs to be used as inputs for current transaction
+ pub inputs: Vec<TransactionInput>,
+ /// UTXOs to be created as a result of current transaction dispatch
+ pub outputs: Vec<TransactionOutput>,
+}
+
+/// [2-data-structure]
/// Single transaction input that refers to one UTXO
-// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-// #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
-// pub struct TransactionInput {
-// /// Reference to an UTXO to be spent
-// pub outpoint: H256,
-
-// /// Proof that transaction owner is authorized to spend referred UTXO &
-// /// that the entire transaction is untampered
-// pub sigscript: H512,
-// }
-
-/// TODO [2-data-structure]
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
+pub struct TransactionInput {
+ /// Reference to an UTXO to be spent
+ pub outpoint: H256,
+
+ /// Proof that transaction owner is authorized to spend referred UTXO &
+ /// that the entire transaction is untampered
+ pub sigscript: H512,
+}
+
+/// [2-data-structure]
/// Single transaction output to create upon transaction dispatch
-// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
-// #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
-// pub struct TransactionOutput {
-// /// Value associated with this output
-// pub value: Value,
-
-// /// Public key associated with this output. In order to spend this output
-// /// owner must provide a proof by hashing the whole `Transaction` and
-// /// signing it with a corresponding private key.
-// pub pubkey: H256,
-// }
+#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
+#[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
+pub struct TransactionOutput {
+ /// Value associated with this output
+ pub value: Value,
+
+ /// Public key associated with this output. In order to spend this output
+ /// owner must provide a proof by hashing the whole `Transaction` and
+ /// signing it with a corresponding private key.
+ pub pubkey: H256,
+}
#[frame_support::pallet(dev_mode)]
pub mod pallet {
@@ -64,140 +64,140 @@ pub mod pallet {
use super::*;
- /// TODO [2-data-structure]
+ /// [2-data-structure]
#[pallet::config]
pub trait Config: frame_system::Config {
- // /// Because this pallet emits events, it depends on the runtime's definition of an event.
- // /// Read more: https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_runtime_types/index.html
- // type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
-
- // /// A source to determine the block author
- // /// Read more: `runtime/src/block_author.rs`
- // /// Pallet loosely coupling
- // /// https://polkadot-blockchain-academy.github.io/pba-book/frame/coupling/page.html#loosely-coupled-pallets
- // type BlockAuthor: BlockAuthor;
-
- // /// A source to determine the issuance portion of the block reward
- // type Issuance: Issuance<BlockNumberFor<Self>, Value>;
+ /// Because this pallet emits events, it depends on the runtime's definition of an event.
+ /// Read more: https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_runtime_types/index.html
+ type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
+
+ /// A source to determine the block author
+ /// Read more: `runtime/src/block_author.rs`
+ /// Pallet loosely coupling
+ /// https://polkadot-blockchain-academy.github.io/pba-book/frame/coupling/page.html#loosely-coupled-pallets
+ type BlockAuthor: BlockAuthor;
+
+ /// A source to determine the issuance portion of the block reward
+ type Issuance: Issuance<BlockNumberFor<Self>, Value>;
}
#[pallet::pallet]
pub struct Pallet<T>(_);
- /// TODO [2-data-structure]
+ /// [2-data-structure]
/// Total reward value to be redistributed among authorities.
/// It is accumulated from transactions during block execution
/// and then dispersed to validators on block finalization.
- // #[pallet::storage]
- // #[pallet::getter(fn total_reward)]
- // pub type TotalReward<T: Config> = StorageValue<_, Value, ValueQuery>;
+ #[pallet::storage]
+ #[pallet::getter(fn total_reward)]
+ pub type TotalReward<T: Config> = StorageValue<_, Value, ValueQuery>;
- /// TODO [2-data-structure]
+ /// [2-data-structure]
/// All valid unspent transaction outputs are stored in this map.
/// Initial set of UTXO is populated from the list stored in genesis.
/// We use the identity hasher here because the cryptographic hashing is
/// done explicitly.
/// Mapping from `BlakeTwo256::hash_of(transaction, index)` to `TransactionOutput`
- // #[pallet::storage]
- // #[pallet::getter(fn utxo_store)]
- // pub type UtxoStore<T: Config> = StorageMap<
- // Hasher = Identity,
- // Key = H256,
- // Value = TransactionOutput,
- // QueryKind = OptionQuery,
- // >;
+ #[pallet::storage]
+ #[pallet::getter(fn utxo_store)]
+ pub type UtxoStore<T: Config> = StorageMap<
+ Hasher = Identity,
+ Key = H256,
+ Value = TransactionOutput,
+ QueryKind = OptionQuery,
+ >;
- /// TODO [2-data-structure]
+ /// [2-data-structure]
/// Pallets use events to inform users when important changes are made.
- // #[pallet::event]
- // #[pallet::generate_deposit(pub(super) fn deposit_event)]
- // pub enum Event<T: Config> {
- // /// Dispatch transaction successful
- // TransactionSuccess(Transaction),
- // /// UTXO out processed
- // TransactionOutputProcessed(H256),
- // /// Reward distributed to `BlockAuthor`
- // RewardDistributed(Value, H256),
- // /// Faucet to `To`
- // Faucet(Value, H256),
- // /// No one get reward
- // RewardWasted,
- // }
-
-
- /// TODO [2-data-structure]
+ #[pallet::event]
+ #[pallet::generate_deposit(pub(super) fn deposit_event)]
+ pub enum Event<T: Config> {
+ /// Dispatch transaction successful
+ TransactionSuccess(Transaction),
+ /// UTXO out processed
+ TransactionOutputProcessed(H256),
+ /// Reward distributed to `BlockAuthor`
+ RewardDistributed(Value, H256),
+ /// Faucet to `To`
+ Faucet(Value, H256),
+ /// No one get reward
+ RewardWasted,
+ }
+
+
+ /// [2-data-structure]
/// Errors inform users that something went wrong.
- // #[pallet::error]
- // pub enum Error<T> {
- // /// Missing `Transaction` Input
- // MissingInput,
- // /// Reward overflow
- // RewardOverflow,
- // /// Maximum transaction depth
- // MaximumTransactionDepth,
- // /// Empty input
- // EmptyInput,
- // /// Empty output
- // EmptyOutput,
- // /// Each input must only be used once
- // DuplicatedInput,
- // /// Each output must be defined only once
- // DuplicatedOutput,
- // /// Input value is overflow
- // InputOverflow,
- // /// Output value is overflow
- // OutputOverflow,
- // /// Output spent must lte than Input spent
- // OutputOverInput,
- // /// Zero amount spent
- // ZeroAmount,
- // /// Invalid signature
- // InvalidSignature,
- // }
-
-
- /// TODO [2-data-structure]
+ #[pallet::error]
+ pub enum Error<T> {
+ /// Missing `Transaction` Input
+ MissingInput,
+ /// Reward overflow
+ RewardOverflow,
+ /// Maximum transaction depth
+ MaximumTransactionDepth,
+ /// Empty input
+ EmptyInput,
+ /// Empty output
+ EmptyOutput,
+ /// Each input must only be used once
+ DuplicatedInput,
+ /// Each output must be defined only once
+ DuplicatedOutput,
+ /// Input value is overflow
+ InputOverflow,
+ /// Output value is overflow
+ OutputOverflow,
+ /// Output spent must lte than Input spent
+ OutputOverInput,
+ /// Zero amount spent
+ ZeroAmount,
+ /// Invalid signature
+ InvalidSignature,
+ }
+
+
+ /// [2-data-structure]
/// Define extrinsics / dispatchable function
- // #[pallet::call]
- // impl<T: Config> Pallet<T> {
- // pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult {
- // todo!();
- // }
+ #[pallet::call]
+ impl<T: Config> Pallet<T> {
+ pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult {
+ todo!();
+ }
- // }
+ }
- /// TODO [2-data-structure]
+ /// [2-data-structure]
/// Define intrinsics
- // impl<T: Config> Pallet<T> {
- // /// Implement spend logic, update storage to reflect changes made by transaction
- // /// Where each UTXO key is a hash of the entire transaction and its order in the `TransactionOutputs` vector
- // fn do_spend(transaction: &Transaction, reward: Value) -> DispatchResult {
- // todo!();
- // }
-
- // /// Redistribute combined reward value to block Author
- // fn disperse_reward(author: &Public) {
- // todo!();
- // }
-
- // /// Mutate storage, insert / update new UTXOs
- // fn store_utxo(utxo: &TransactionOutput, hash: H256) {
- // todo!();
- // }
-
- // /// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash.
- // fn get_simple_transaction(transaction: &Transaction) -> Vec<u8> {
- // todo!();
- // }
-
- // /// Check transaction for validity, errors, & race conditions
- // /// Called by both transaction pool and runtime execution
- // pub fn validate_transaction(
- // transaction: &Transaction,
- // ) -> Result<ValidTransaction, &'static str> {
- // todo!();
- // }
- // }
+ impl<T: Config> Pallet<T> {
+ /// Implement spend logic, update storage to reflect changes made by transaction
+ /// Where each UTXO key is a hash of the entire transaction and its order in the `TransactionOutputs` vector
+ fn do_spend(transaction: &Transaction, reward: Value) -> DispatchResult {
+ todo!();
+ }
+
+ /// Redistribute combined reward value to block Author
+ fn disperse_reward(author: &Public) {
+ todo!();
+ }
+
+ /// Mutate storage, insert / update new UTXOs
+ fn store_utxo(utxo: &TransactionOutput, hash: H256) {
+ todo!();
+ }
+
+ /// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash.
+ fn get_simple_transaction(transaction: &Transaction) -> Vec<u8> {
+ todo!();
+ }
+
+ /// Check transaction for validity, errors, & race conditions
+ /// Called by both transaction pool and runtime execution
+ pub fn validate_transaction(
+ transaction: &Transaction,
+ ) -> Result<ValidTransaction, &'static str> {
+ todo!();
+ }
+ }
}
\ No newline at end of file
Interacting with UTXO
[!Note] Resolve all TODO in
runtime/src/utxo.rs
to complete this step.
Now that we have established the basics of our UTXO module, let's add ways to interact with it.
Reading Materials
Dispatchable / Call / Extrinsic functions:
[!Note] Note that we'll implement UTXO pallet in
dev_mode
, it is not necessary to specifyweight
andcall_index
. However, feel free to do it if you want.
Implement for Pallet UTXO
Implement spend
extrinsic. Highly recommend that keep the function simple and move all logics to intrinsics named with prefix do_...
, execute_...
(or whatever prefix you want 👀).
#![allow(unused)] fn main() { pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult { // validate transaction let transaction_validity = Self::validate_transaction(&transaction)?; ensure!( transaction_validity.requires.is_empty(), Error::<T>::MissingInput ); // implement logic Self::do_spend(&transaction, transaction_validity.priority as Value)?; // emit event Self::deposit_event(Event::<T>::TransactionSuccess(transaction)); Ok(()) } }
Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash.
Just is for demo usage, feel free to add logic of using additional salt yourself 😉.
#![allow(unused)] fn main() { fn get_simple_transaction(transaction: &Transaction) -> Vec<u8> { let mut trx = transaction.clone(); for input in trx.inputs.iter_mut() { input.sigscript = H512::zero(); } trx.encode() } }
Implement validate_transaction intrinsic with criteria:
- Inputs and outputs are not empty
- Each input is used exactly once
- All inputs match to existing, unspent and unlocked outputs
- Sum of input values does not overflow
- Each output is defined exactly once and has nonzero value
- Sum of output values does not overflow
- Total output value must not exceed total input value
#![allow(unused)] fn main() { /// Check transaction for validity, errors, & race conditions /// Called by both transaction pool and runtime execution pub fn validate_transaction( transaction: &Transaction, ) -> Result<ValidTransaction, &'static str> { // Check inputs and outputs are not empty ensure!(!transaction.inputs.is_empty(), Error::<T>::EmptyInput); ensure!(!transaction.outputs.is_empty(), Error::<T>::EmptyOutput); // Check each input is used exactly once { let input_set: BTreeMap<_, ()> = transaction.inputs.iter().map(|input| (input, ())).collect(); ensure!( input_set.len() == transaction.inputs.len(), Error::<T>::DuplicatedInput ); } { let output_set: BTreeMap<_, ()> = transaction .outputs .iter() .map(|output| (output, ())) .collect(); ensure!( output_set.len() == transaction.outputs.len(), Error::<T>::DuplicatedOutput ); } let mut total_input: Value = 0; let mut total_output: Value = 0; let mut output_index: u64 = 0; let simple_transaction = Self::get_simple_transaction(transaction); // Variables sent to transaction pool let mut missing_utxos = Vec::new(); let mut new_utxos = Vec::new(); let mut reward = 0; for input in transaction.inputs.iter() { // Check all inputs match to existing, unspent and unlocked outputs if let Some(input_utxo) = UtxoStore::<T>::get(&input.outpoint) { // Check provided signatures are valid let is_valid_sig = sp_io::crypto::sr25519_verify( &Signature::from_raw(*input.sigscript.as_fixed_bytes()), &simple_transaction, &Public::from_h256(input_utxo.pubkey), ); ensure!(is_valid_sig, Error::<T>::InvalidSignature); // Check sum of input values does not overflow total_input = total_input .checked_add(input_utxo.value) .ok_or(Error::<T>::InputOverflow)?; } else { missing_utxos.push(input.outpoint.clone().as_fixed_bytes().to_vec()); } } // Check each output is defined exactly once and has nonzero value for output in transaction.outputs.iter() { ensure!(output.value > 0, Error::<T>::ZeroAmount); let hash = BlakeTwo256::hash_of(&(&transaction.encode(), output_index)); output_index = output_index .checked_add(1) .ok_or(Error::<T>::MaximumTransactionDepth)?; // Check new outputs do not collide with existing ones ensure!( !UtxoStore::<T>::contains_key(hash), Error::<T>::DuplicatedOutput ); // Check sum of output values does not overflow total_output = total_output .checked_add(output.value) .ok_or(Error::<T>::OutputOverflow)?; new_utxos.push(hash.as_fixed_bytes().to_vec()); } // If no race condition, check the math if missing_utxos.is_empty() { // Check total output value must not exceed total input value ensure!(total_input >= total_output, Error::<T>::OutputOverInput); reward = total_input .checked_sub(total_output) .ok_or(Error::<T>::RewardOverflow)?; } // Returns transaction details Ok(ValidTransaction { requires: missing_utxos, provides: new_utxos, priority: reward as u64, longevity: TransactionLongevity::max_value(), propagate: true, }) } }
Implement do_spend
intrinsic for spend
extrinsic
#![allow(unused)] fn main() { /// Implement spend logic, update storage to reflect changes made by transaction /// Where each UTXO key is a hash of the entire transaction and its order in the `TransactionOutputs` vector fn do_spend(transaction: &Transaction, reward: Value) -> DispatchResult { // Calculate new reward total. The rest of `total_input - total_output` will be used for block reward. let new_total = TotalReward::<T>::get() .checked_add(reward) .ok_or(Error::<T>::RewardOverflow)?; TotalReward::<T>::put(new_total); // Removing spent UTXOs for input in &transaction.inputs { UtxoStore::<T>::remove(input.outpoint); } let mut index: u64 = 0; for output in &transaction.outputs { let hash = BlakeTwo256::hash_of(&(&transaction.encode(), index)); // validated before, this is safe index = index .checked_add(1) .ok_or(Error::<T>::MaximumTransactionDepth) .unwrap(); Self::store_utxo(output, hash); // Optional, this event I used for log Self::deposit_event(Event::TransactionOutputProcessed(hash)); } Ok(()) } }
Implement store_utxo
for storage mutation
#![allow(unused)] fn main() { fn store_utxo(utxo: &TransactionOutput, hash: H256) { // TODO [3-spend-utxo] UtxoStore::<T>::insert(hash, utxo); // further update 😉 } }
Build the code
cargo build --release
Awesome work completing this step! 🚀 You're doing great. Now, let's advance to next step.
#![allow(unused)] fn main() { // We make sure this pallet uses `no_std` for compiling to Wasm. #![cfg_attr(not(feature = "std"), no_std)] use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_core::{ sr25519::{Public, Signature}, ByteArray, H256, H512, }; use sp_runtime::traits::{BlakeTwo256, Hash, SaturatedConversion}; use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; use super::{block_author::BlockAuthor, issuance::Issuance}; pub use pallet::*; /// [2-data-structure] pub type Value = u128; /// [2-data-structure] /// Single transaction to be dispatched #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct Transaction { /// UTXOs to be used as inputs for current transaction pub inputs: Vec<TransactionInput>, /// UTXOs to be created as a result of current transaction dispatch pub outputs: Vec<TransactionOutput>, } /// [2-data-structure] /// Single transaction input that refers to one UTXO #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct TransactionInput { /// Reference to an UTXO to be spent pub outpoint: H256, /// Proof that transaction owner is authorized to spend referred UTXO & /// that the entire transaction is untampered pub sigscript: H512, } /// [2-data-structure] /// Single transaction output to create upon transaction dispatch #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct TransactionOutput { /// Value associated with this output pub value: Value, /// Public key associated with this output. In order to spend this output /// owner must provide a proof by hashing the whole `Transaction` and /// signing it with a corresponding private key. pub pubkey: H256, } #[frame_support::pallet(dev_mode)] pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use super::*; /// [2-data-structure] #[pallet::config] pub trait Config: frame_system::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. /// Read more: https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_runtime_types/index.html type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; /// A source to determine the block author type BlockAuthor: BlockAuthor; /// A source to determine the issuance portion of the block reward type Issuance: Issuance<BlockNumberFor<Self>, Value>; } #[pallet::pallet] pub struct Pallet<T>(_); /// [2-data-structure] /// Total reward value to be redistributed among authorities. /// It is accumulated from transactions during block execution /// and then dispersed to validators on block finalization. #[pallet::storage] #[pallet::getter(fn total_reward)] pub type TotalReward<T: Config> = StorageValue<_, Value, ValueQuery>; /// [2-data-structure] /// All valid unspent transaction outputs are stored in this map. /// Initial set of UTXO is populated from the list stored in genesis. /// We use the identity hasher here because the cryptographic hashing is /// done explicitly. /// Mapping from `BlakeTwo256::hash_of(transaction, index)` to `TransactionOutput` #[pallet::storage] #[pallet::getter(fn utxo_store)] pub type UtxoStore<T: Config> = StorageMap< Hasher = Identity, Key = H256, Value = TransactionOutput, QueryKind = OptionQuery, >; /// [2-data-structure] /// Pallets use events to inform users when important changes are made. #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config> { /// Dispatch transaction successful TransactionSuccess(Transaction), /// UTXO out processed TransactionOutputProcessed(H256), /// Reward distributed to `BlockAuthor` RewardDistributed(Value, H256), /// Faucet to `To` Faucet(Value, H256), /// No one get reward RewardWasted, } /// [2-data-structure] /// Errors inform users that something went wrong. #[pallet::error] pub enum Error<T> { /// Missing `Transaction` Input MissingInput, /// Reward overflow RewardOverflow, /// Maximum transaction depth MaximumTransactionDepth, /// Empty input EmptyInput, /// Empty output EmptyOutput, /// Each input must only be used once DuplicatedInput, /// Each output must be defined only once DuplicatedOutput, /// Input value is overflow InputOverflow, /// Output value is overflow OutputOverflow, /// Output spent must lte than Input spent OutputOverInput, /// Zero amount spent ZeroAmount, /// Invalid signature InvalidSignature, } /// [2-data-structure] /// Define extrinsics / dispatchable function #[pallet::call] impl<T: Config> Pallet<T> { pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult { // TODO remove this todo!() // TODO [3-spend-utxo] // validate transaction // let transaction_validity = Self::validate_transaction(&transaction)?; // ensure!( // transaction_validity.requires.is_empty(), // Error::<T>::MissingInput // ); // // implement logic // Self::do_spend(&transaction, transaction_validity.priority as Value)?; // // emit event // Self::deposit_event(Event::<T>::TransactionSuccess(transaction)); // Ok(()) } } /// [2-data-structure] /// Define intrinsics impl<T: Config> Pallet<T> { /// Implement spend logic, update storage to reflect changes made by transaction /// Where each UTXO key is a hash of the entire transaction and its order in the `TransactionOutputs` vector fn do_spend(transaction: &Transaction, reward: Value) -> DispatchResult { // TODO remove this todo!(); // TODO [3-spend-utxo] // Calculate new reward total. The rest of `total_input - total_output` will be used for block reward. // let new_total = TotalReward::<T>::get() // .checked_add(reward) // .ok_or(Error::<T>::RewardOverflow)?; // TotalReward::<T>::put(new_total); // // Removing spent UTXOs // for input in &transaction.inputs { // UtxoStore::<T>::remove(input.outpoint); // } // let mut index: u64 = 0; // for output in &transaction.outputs { // let hash = BlakeTwo256::hash_of(&(&transaction.encode(), index)); // // validated before, this is safe // index = index // .checked_add(1) // .ok_or(Error::<T>::MaximumTransactionDepth) // .unwrap(); // Self::store_utxo(output, hash); // Self::deposit_event(Event::TransactionOutputProcessed(hash)); // } // Ok(()) } /// Redistribute combined reward value to block Author fn disperse_reward(author: &Public) { todo!(); } /// Mutate storage, insert / update new UTXOs fn store_utxo(utxo: &TransactionOutput, hash: H256) { // TODO remove this todo!(); // // TODO [3-spend-utxo] // UtxoStore::<T>::insert(hash, utxo); // // further update 😉 } /// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash. fn get_simple_transaction(transaction: &Transaction) -> Vec<u8> { // TODO remove this todo!(); // // TODO [3-spend-utxo] // let mut trx = transaction.clone(); // for input in trx.inputs.iter_mut() { // input.sigscript = H512::zero(); // } // trx.encode() } /// Check transaction for validity, errors, & race conditions /// Called by both transaction pool and runtime execution pub fn validate_transaction( transaction: &Transaction, ) -> Result<ValidTransaction, &'static str> { // TODO remove this todo!(); // TODO [3-spend-utxo] // // Check inputs and outputs are not empty // ensure!(!transaction.inputs.is_empty(), Error::<T>::EmptyInput); // ensure!(!transaction.outputs.is_empty(), Error::<T>::EmptyOutput); // // Check each input is used exactly once // { // let input_set: BTreeMap<_, ()> = // transaction.inputs.iter().map(|input| (input, ())).collect(); // ensure!( // input_set.len() == transaction.inputs.len(), // Error::<T>::DuplicatedInput // ); // } // { // let output_set: BTreeMap<_, ()> = transaction // .outputs // .iter() // .map(|output| (output, ())) // .collect(); // ensure!( // output_set.len() == transaction.outputs.len(), // Error::<T>::DuplicatedOutput // ); // } // let mut total_input: Value = 0; // let mut total_output: Value = 0; // let mut output_index: u64 = 0; // let simple_transaction = Self::get_simple_transaction(transaction); // // Variables sent to transaction pool // let mut missing_utxos = Vec::new(); // let mut new_utxos = Vec::new(); // let mut reward = 0; // for input in transaction.inputs.iter() { // // Check all inputs match to existing, unspent and unlocked outputs // if let Some(input_utxo) = UtxoStore::<T>::get(&input.outpoint) { // // Check provided signatures are valid // let is_valid_sig = sp_io::crypto::sr25519_verify( // &Signature::from_raw(*input.sigscript.as_fixed_bytes()), // &simple_transaction, // &Public::from_h256(input_utxo.pubkey), // ); // ensure!(is_valid_sig, Error::<T>::InvalidSignature); // // Check sum of input values does not overflow // total_input = total_input // .checked_add(input_utxo.value) // .ok_or(Error::<T>::InputOverflow)?; // } else { // missing_utxos.push(input.outpoint.clone().as_fixed_bytes().to_vec()); // } // } // // Check each output is defined exactly once and has nonzero value // for output in transaction.outputs.iter() { // ensure!(output.value > 0, Error::<T>::ZeroAmount); // let hash = BlakeTwo256::hash_of(&(&transaction.encode(), output_index)); // output_index = output_index // .checked_add(1) // .ok_or(Error::<T>::MaximumTransactionDepth)?; // // Check new outputs do not collide with existing ones // ensure!( // !UtxoStore::<T>::contains_key(hash), // Error::<T>::DuplicatedOutput // ); // // Check sum of output values does not overflow // total_output = total_output // .checked_add(output.value) // .ok_or(Error::<T>::OutputOverflow)?; // new_utxos.push(hash.as_fixed_bytes().to_vec()); // } // // If no race condition, check the math // if missing_utxos.is_empty() { // // Check total output value must not exceed total input value // ensure!(total_input >= total_output, Error::<T>::OutputOverInput); // reward = total_input // .checked_sub(total_output) // .ok_or(Error::<T>::RewardOverflow)?; // } // // Returns transaction details // Ok(ValidTransaction { // requires: missing_utxos, // provides: new_utxos, // priority: reward as u64, // longevity: TransactionLongevity::max_value(), // propagate: true, // }) } } } }
#![allow(unused)] fn main() { // We make sure this pallet uses `no_std` for compiling to Wasm. #![cfg_attr(not(feature = "std"), no_std)] use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_core::{ sr25519::{Public, Signature}, ByteArray, H256, H512, }; use sp_runtime::traits::{BlakeTwo256, Hash, SaturatedConversion}; use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; use super::{block_author::BlockAuthor, issuance::Issuance}; pub use pallet::*; /// [2-data-structure] pub type Value = u128; /// [2-data-structure] /// Single transaction to be dispatched #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct Transaction { /// UTXOs to be used as inputs for current transaction pub inputs: Vec<TransactionInput>, /// UTXOs to be created as a result of current transaction dispatch pub outputs: Vec<TransactionOutput>, } /// [2-data-structure] /// Single transaction input that refers to one UTXO #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct TransactionInput { /// Reference to an UTXO to be spent pub outpoint: H256, /// Proof that transaction owner is authorized to spend referred UTXO & /// that the entire transaction is untampered pub sigscript: H512, } /// [2-data-structure] /// Single transaction output to create upon transaction dispatch #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct TransactionOutput { /// Value associated with this output pub value: Value, /// Public key associated with this output. In order to spend this output /// owner must provide a proof by hashing the whole `Transaction` and /// signing it with a corresponding private key. pub pubkey: H256, } #[frame_support::pallet(dev_mode)] pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use super::*; /// [2-data-structure] #[pallet::config] pub trait Config: frame_system::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. /// Read more: https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_runtime_types/index.html type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; /// A source to determine the block author type BlockAuthor: BlockAuthor; /// A source to determine the issuance portion of the block reward type Issuance: Issuance<BlockNumberFor<Self>, Value>; } #[pallet::pallet] pub struct Pallet<T>(_); /// [2-data-structure] /// Total reward value to be redistributed among authorities. /// It is accumulated from transactions during block execution /// and then dispersed to validators on block finalization. #[pallet::storage] #[pallet::getter(fn total_reward)] pub type TotalReward<T: Config> = StorageValue<_, Value, ValueQuery>; /// [2-data-structure] /// All valid unspent transaction outputs are stored in this map. /// Initial set of UTXO is populated from the list stored in genesis. /// We use the identity hasher here because the cryptographic hashing is /// done explicitly. /// Mapping from `BlakeTwo256::hash_of(transaction, index)` to `TransactionOutput` #[pallet::storage] #[pallet::getter(fn utxo_store)] pub type UtxoStore<T: Config> = StorageMap< Hasher = Identity, Key = H256, Value = TransactionOutput, QueryKind = OptionQuery, >; /// [2-data-structure] /// Pallets use events to inform users when important changes are made. #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config> { /// Dispatch transaction successful TransactionSuccess(Transaction), /// UTXO out processed TransactionOutputProcessed(H256), /// Reward distributed to `BlockAuthor` RewardDistributed(Value, H256), /// Faucet to `To` Faucet(Value, H256), /// No one get reward RewardWasted, } /// [2-data-structure] /// Errors inform users that something went wrong. #[pallet::error] pub enum Error<T> { /// Missing `Transaction` Input MissingInput, /// Reward overflow RewardOverflow, /// Maximum transaction depth MaximumTransactionDepth, /// Empty input EmptyInput, /// Empty output EmptyOutput, /// Each input must only be used once DuplicatedInput, /// Each output must be defined only once DuplicatedOutput, /// Input value is overflow InputOverflow, /// Output value is overflow OutputOverflow, /// Output spent must lte than Input spent OutputOverInput, /// Zero amount spent ZeroAmount, /// Invalid signature InvalidSignature, } /// [2-data-structure] /// Define extrinsics / dispatchable function #[pallet::call] impl<T: Config> Pallet<T> { pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult { // [3-spend-utxo] // validate transaction let transaction_validity = Self::validate_transaction(&transaction)?; ensure!( transaction_validity.requires.is_empty(), Error::<T>::MissingInput ); // implement logic Self::do_spend(&transaction, transaction_validity.priority as Value)?; // emit event Self::deposit_event(Event::<T>::TransactionSuccess(transaction)); Ok(()) } } /// [2-data-structure] /// Define intrinsics impl<T: Config> Pallet<T> { /// Implement spend logic, update storage to reflect changes made by transaction /// Where each UTXO key is a hash of the entire transaction and its order in the `TransactionOutputs` vector fn do_spend(transaction: &Transaction, reward: Value) -> DispatchResult { // [3-spend-utxo] // Calculate new reward total. The rest of `total_input - total_output` will be used for block reward. let new_total = TotalReward::<T>::get() .checked_add(reward) .ok_or(Error::<T>::RewardOverflow)?; TotalReward::<T>::put(new_total); // Removing spent UTXOs for input in &transaction.inputs { UtxoStore::<T>::remove(input.outpoint); } let mut index: u64 = 0; for output in &transaction.outputs { let hash = BlakeTwo256::hash_of(&(&transaction.encode(), index)); // validated before, this is safe index = index .checked_add(1) .ok_or(Error::<T>::MaximumTransactionDepth) .unwrap(); Self::store_utxo(output, hash); Self::deposit_event(Event::TransactionOutputProcessed(hash)); } Ok(()) } /// Redistribute combined reward value to block Author fn disperse_reward(author: &Public) { todo!(); } /// Mutate storage, insert / update new UTXOs fn store_utxo(utxo: &TransactionOutput, hash: H256) { // [3-spend-utxo] UtxoStore::<T>::insert(hash, utxo); // // further update 😉 } /// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash. fn get_simple_transaction(transaction: &Transaction) -> Vec<u8> { // [3-spend-utxo] let mut trx = transaction.clone(); for input in trx.inputs.iter_mut() { input.sigscript = H512::zero(); } trx.encode() } /// Check transaction for validity, errors, & race conditions /// Called by both transaction pool and runtime execution pub fn validate_transaction( transaction: &Transaction, ) -> Result<ValidTransaction, &'static str> { // [3-spend-utxo] // Check inputs and outputs are not empty ensure!(!transaction.inputs.is_empty(), Error::<T>::EmptyInput); ensure!(!transaction.outputs.is_empty(), Error::<T>::EmptyOutput); // Check each input is used exactly once { let input_set: BTreeMap<_, ()> = transaction.inputs.iter().map(|input| (input, ())).collect(); ensure!( input_set.len() == transaction.inputs.len(), Error::<T>::DuplicatedInput ); } { let output_set: BTreeMap<_, ()> = transaction .outputs .iter() .map(|output| (output, ())) .collect(); ensure!( output_set.len() == transaction.outputs.len(), Error::<T>::DuplicatedOutput ); } let mut total_input: Value = 0; let mut total_output: Value = 0; let mut output_index: u64 = 0; let simple_transaction = Self::get_simple_transaction(transaction); // Variables sent to transaction pool let mut missing_utxos = Vec::new(); let mut new_utxos = Vec::new(); let mut reward = 0; for input in transaction.inputs.iter() { // Check all inputs match to existing, unspent and unlocked outputs if let Some(input_utxo) = UtxoStore::<T>::get(&input.outpoint) { // Check provided signatures are valid let is_valid_sig = sp_io::crypto::sr25519_verify( &Signature::from_raw(*input.sigscript.as_fixed_bytes()), &simple_transaction, &Public::from_h256(input_utxo.pubkey), ); ensure!(is_valid_sig, Error::<T>::InvalidSignature); // Check sum of input values does not overflow total_input = total_input .checked_add(input_utxo.value) .ok_or(Error::<T>::InputOverflow)?; } else { missing_utxos.push(input.outpoint.clone().as_fixed_bytes().to_vec()); } } // Check each output is defined exactly once and has nonzero value for output in transaction.outputs.iter() { ensure!(output.value > 0, Error::<T>::ZeroAmount); let hash = BlakeTwo256::hash_of(&(&transaction.encode(), output_index)); output_index = output_index .checked_add(1) .ok_or(Error::<T>::MaximumTransactionDepth)?; // Check new outputs do not collide with existing ones ensure!( !UtxoStore::<T>::contains_key(hash), Error::<T>::DuplicatedOutput ); // Check sum of output values does not overflow total_output = total_output .checked_add(output.value) .ok_or(Error::<T>::OutputOverflow)?; new_utxos.push(hash.as_fixed_bytes().to_vec()); } // If no race condition, check the math if missing_utxos.is_empty() { // Check total output value must not exceed total input value ensure!(total_input >= total_output, Error::<T>::OutputOverInput); reward = total_input .checked_sub(total_output) .ok_or(Error::<T>::RewardOverflow)?; } // Returns transaction details Ok(ValidTransaction { requires: missing_utxos, provides: new_utxos, priority: reward as u64, longevity: TransactionLongevity::max_value(), propagate: true, }) } } } }
diff --git a/runtime/src/utxo.rs b/runtime/src/utxo.rs
index 69a9535..a36637d 100644
--- a/runtime/src/utxo.rs
+++ b/runtime/src/utxo.rs
@@ -16,10 +16,10 @@ use super::{block_author::BlockAuthor, issuance::Issuance};
pub use pallet::*;
-/// [2-data-structure]
+/// [2-data-structure]
pub type Value = u128;
-/// [2-data-structure]
+/// [2-data-structure]
/// Single transaction to be dispatched
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
@@ -30,7 +30,7 @@ pub struct Transaction {
pub outputs: Vec<TransactionOutput>,
}
-/// [2-data-structure]
+/// [2-data-structure]
/// Single transaction input that refers to one UTXO
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
@@ -43,7 +43,7 @@ pub struct TransactionInput {
pub sigscript: H512,
}
-/// [2-data-structure]
+/// [2-data-structure]
/// Single transaction output to create upon transaction dispatch
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)]
@@ -64,7 +64,7 @@ pub mod pallet {
use super::*;
- /// [2-data-structure]
+ /// [2-data-structure]
#[pallet::config]
pub trait Config: frame_system::Config {
/// Because this pallet emits events, it depends on the runtime's definition of an event.
@@ -72,9 +72,6 @@ pub mod pallet {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
/// A source to determine the block author
- /// Read more: `runtime/src/block_author.rs`
- /// Pallet loosely coupling
- /// https://polkadot-blockchain-academy.github.io/pba-book/frame/coupling/page.html#loosely-coupled-pallets
type BlockAuthor: BlockAuthor;
/// A source to determine the issuance portion of the block reward
@@ -85,7 +82,7 @@ pub mod pallet {
pub struct Pallet<T>(_);
- /// [2-data-structure]
+ /// [2-data-structure]
/// Total reward value to be redistributed among authorities.
/// It is accumulated from transactions during block execution
/// and then dispersed to validators on block finalization.
@@ -93,7 +90,7 @@ pub mod pallet {
#[pallet::getter(fn total_reward)]
pub type TotalReward<T: Config> = StorageValue<_, Value, ValueQuery>;
- /// [2-data-structure]
+ /// [2-data-structure]
/// All valid unspent transaction outputs are stored in this map.
/// Initial set of UTXO is populated from the list stored in genesis.
/// We use the identity hasher here because the cryptographic hashing is
@@ -109,7 +106,7 @@ pub mod pallet {
>;
- /// [2-data-structure]
+ /// [2-data-structure]
/// Pallets use events to inform users when important changes are made.
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
@@ -127,7 +124,7 @@ pub mod pallet {
}
- /// [2-data-structure]
+ /// [2-data-structure]
/// Errors inform users that something went wrong.
#[pallet::error]
pub enum Error<T> {
@@ -158,23 +155,65 @@ pub mod pallet {
}
- /// [2-data-structure]
+ /// [2-data-structure]
/// Define extrinsics / dispatchable function
#[pallet::call]
impl<T: Config> Pallet<T> {
pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult {
- todo!();
- }
+ // TODO remove this
+ todo!()
+
+ // TODO [3-spend-utxo]
+ // validate transaction
+ // let transaction_validity = Self::validate_transaction(&transaction)?;
+ // ensure!(
+ // transaction_validity.requires.is_empty(),
+ // Error::<T>::MissingInput
+ // );
+ // // implement logic
+ // Self::do_spend(&transaction, transaction_validity.priority as Value)?;
+ // // emit event
+ // Self::deposit_event(Event::<T>::TransactionSuccess(transaction));
+
+ // Ok(())
+ }
}
- /// [2-data-structure]
+ /// [2-data-structure]
/// Define intrinsics
impl<T: Config> Pallet<T> {
/// Implement spend logic, update storage to reflect changes made by transaction
/// Where each UTXO key is a hash of the entire transaction and its order in the `TransactionOutputs` vector
fn do_spend(transaction: &Transaction, reward: Value) -> DispatchResult {
+ // TODO remove this
todo!();
+
+ // TODO [3-spend-utxo]
+ // Calculate new reward total. The rest of `total_input - total_output` will be used for block reward.
+ // let new_total = TotalReward::<T>::get()
+ // .checked_add(reward)
+ // .ok_or(Error::<T>::RewardOverflow)?;
+ // TotalReward::<T>::put(new_total);
+
+ // // Removing spent UTXOs
+ // for input in &transaction.inputs {
+ // UtxoStore::<T>::remove(input.outpoint);
+ // }
+
+ // let mut index: u64 = 0;
+ // for output in &transaction.outputs {
+ // let hash = BlakeTwo256::hash_of(&(&transaction.encode(), index));
+ // // validated before, this is safe
+ // index = index
+ // .checked_add(1)
+ // .ok_or(Error::<T>::MaximumTransactionDepth)
+ // .unwrap();
+ // Self::store_utxo(output, hash);
+ // Self::deposit_event(Event::TransactionOutputProcessed(hash));
+ // }
+
+ // Ok(())
}
/// Redistribute combined reward value to block Author
@@ -184,12 +223,27 @@ pub mod pallet {
/// Mutate storage, insert / update new UTXOs
fn store_utxo(utxo: &TransactionOutput, hash: H256) {
+ // TODO remove this
todo!();
+
+ // // TODO [3-spend-utxo]
+ // UtxoStore::<T>::insert(hash, utxo);
+
+ // // further update 😉
}
/// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash.
fn get_simple_transaction(transaction: &Transaction) -> Vec<u8> {
+ // TODO remove this
todo!();
+
+ // // TODO [3-spend-utxo]
+ // let mut trx = transaction.clone();
+ // for input in trx.inputs.iter_mut() {
+ // input.sigscript = H512::zero();
+ // }
+
+ // trx.encode()
}
/// Check transaction for validity, errors, & race conditions
@@ -197,7 +251,100 @@ pub mod pallet {
pub fn validate_transaction(
transaction: &Transaction,
) -> Result<ValidTransaction, &'static str> {
+ // TODO remove this
todo!();
+
+ // TODO [3-spend-utxo]
+ // // Check inputs and outputs are not empty
+ // ensure!(!transaction.inputs.is_empty(), Error::<T>::EmptyInput);
+ // ensure!(!transaction.outputs.is_empty(), Error::<T>::EmptyOutput);
+
+ // // Check each input is used exactly once
+ // {
+ // let input_set: BTreeMap<_, ()> =
+ // transaction.inputs.iter().map(|input| (input, ())).collect();
+ // ensure!(
+ // input_set.len() == transaction.inputs.len(),
+ // Error::<T>::DuplicatedInput
+ // );
+ // }
+ // {
+ // let output_set: BTreeMap<_, ()> = transaction
+ // .outputs
+ // .iter()
+ // .map(|output| (output, ()))
+ // .collect();
+ // ensure!(
+ // output_set.len() == transaction.outputs.len(),
+ // Error::<T>::DuplicatedOutput
+ // );
+ // }
+
+ // let mut total_input: Value = 0;
+ // let mut total_output: Value = 0;
+ // let mut output_index: u64 = 0;
+ // let simple_transaction = Self::get_simple_transaction(transaction);
+
+ // // Variables sent to transaction pool
+ // let mut missing_utxos = Vec::new();
+ // let mut new_utxos = Vec::new();
+ // let mut reward = 0;
+
+ // for input in transaction.inputs.iter() {
+ // // Check all inputs match to existing, unspent and unlocked outputs
+ // if let Some(input_utxo) = UtxoStore::<T>::get(&input.outpoint) {
+ // // Check provided signatures are valid
+ // let is_valid_sig = sp_io::crypto::sr25519_verify(
+ // &Signature::from_raw(*input.sigscript.as_fixed_bytes()),
+ // &simple_transaction,
+ // &Public::from_h256(input_utxo.pubkey),
+ // );
+ // ensure!(is_valid_sig, Error::<T>::InvalidSignature);
+ // // Check sum of input values does not overflow
+ // total_input = total_input
+ // .checked_add(input_utxo.value)
+ // .ok_or(Error::<T>::InputOverflow)?;
+ // } else {
+ // missing_utxos.push(input.outpoint.clone().as_fixed_bytes().to_vec());
+ // }
+ // }
+
+ // // Check each output is defined exactly once and has nonzero value
+ // for output in transaction.outputs.iter() {
+ // ensure!(output.value > 0, Error::<T>::ZeroAmount);
+ // let hash = BlakeTwo256::hash_of(&(&transaction.encode(), output_index));
+ // output_index = output_index
+ // .checked_add(1)
+ // .ok_or(Error::<T>::MaximumTransactionDepth)?;
+ // // Check new outputs do not collide with existing ones
+ // ensure!(
+ // !UtxoStore::<T>::contains_key(hash),
+ // Error::<T>::DuplicatedOutput
+ // );
+ // // Check sum of output values does not overflow
+ // total_output = total_output
+ // .checked_add(output.value)
+ // .ok_or(Error::<T>::OutputOverflow)?;
+ // new_utxos.push(hash.as_fixed_bytes().to_vec());
+ // }
+
+ // // If no race condition, check the math
+ // if missing_utxos.is_empty() {
+ // // Check total output value must not exceed total input value
+ // ensure!(total_input >= total_output, Error::<T>::OutputOverInput);
+ // reward = total_input
+ // .checked_sub(total_output)
+ // .ok_or(Error::<T>::RewardOverflow)?;
+ // }
+
+ // // Returns transaction details
+ // Ok(ValidTransaction {
+ // requires: missing_utxos,
+ // provides: new_utxos,
+ // priority: reward as u64,
+ // longevity: TransactionLongevity::max_value(),
+ // propagate: true,
+ // })
}
}
}
\ No newline at end of file
diff --git a/runtime/src/utxo.rs b/runtime/src/utxo.rs
index a36637d..00433df 100644
--- a/runtime/src/utxo.rs
+++ b/runtime/src/utxo.rs
@@ -160,23 +160,20 @@ pub mod pallet {
#[pallet::call]
impl<T: Config> Pallet<T> {
pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult {
- // TODO remove this
- todo!()
-
- // TODO [3-spend-utxo]
+ // [3-spend-utxo]
// validate transaction
- // let transaction_validity = Self::validate_transaction(&transaction)?;
- // ensure!(
- // transaction_validity.requires.is_empty(),
- // Error::<T>::MissingInput
- // );
-
- // // implement logic
- // Self::do_spend(&transaction, transaction_validity.priority as Value)?;
- // // emit event
- // Self::deposit_event(Event::<T>::TransactionSuccess(transaction));
-
- // Ok(())
+ let transaction_validity = Self::validate_transaction(&transaction)?;
+ ensure!(
+ transaction_validity.requires.is_empty(),
+ Error::<T>::MissingInput
+ );
+
+ // implement logic
+ Self::do_spend(&transaction, transaction_validity.priority as Value)?;
+ // emit event
+ Self::deposit_event(Event::<T>::TransactionSuccess(transaction));
+
+ Ok(())
}
}
@@ -186,34 +183,31 @@ pub mod pallet {
/// Implement spend logic, update storage to reflect changes made by transaction
/// Where each UTXO key is a hash of the entire transaction and its order in the `TransactionOutputs` vector
fn do_spend(transaction: &Transaction, reward: Value) -> DispatchResult {
- // TODO remove this
- todo!();
-
- // TODO [3-spend-utxo]
+ // [3-spend-utxo]
// Calculate new reward total. The rest of `total_input - total_output` will be used for block reward.
- // let new_total = TotalReward::<T>::get()
- // .checked_add(reward)
- // .ok_or(Error::<T>::RewardOverflow)?;
- // TotalReward::<T>::put(new_total);
-
- // // Removing spent UTXOs
- // for input in &transaction.inputs {
- // UtxoStore::<T>::remove(input.outpoint);
- // }
-
- // let mut index: u64 = 0;
- // for output in &transaction.outputs {
- // let hash = BlakeTwo256::hash_of(&(&transaction.encode(), index));
- // // validated before, this is safe
- // index = index
- // .checked_add(1)
- // .ok_or(Error::<T>::MaximumTransactionDepth)
- // .unwrap();
- // Self::store_utxo(output, hash);
- // Self::deposit_event(Event::TransactionOutputProcessed(hash));
- // }
-
- // Ok(())
+ let new_total = TotalReward::<T>::get()
+ .checked_add(reward)
+ .ok_or(Error::<T>::RewardOverflow)?;
+ TotalReward::<T>::put(new_total);
+
+ // Removing spent UTXOs
+ for input in &transaction.inputs {
+ UtxoStore::<T>::remove(input.outpoint);
+ }
+
+ let mut index: u64 = 0;
+ for output in &transaction.outputs {
+ let hash = BlakeTwo256::hash_of(&(&transaction.encode(), index));
+ // validated before, this is safe
+ index = index
+ .checked_add(1)
+ .ok_or(Error::<T>::MaximumTransactionDepth)
+ .unwrap();
+ Self::store_utxo(output, hash);
+ Self::deposit_event(Event::TransactionOutputProcessed(hash));
+ }
+
+ Ok(())
}
/// Redistribute combined reward value to block Author
@@ -223,27 +217,21 @@ pub mod pallet {
/// Mutate storage, insert / update new UTXOs
fn store_utxo(utxo: &TransactionOutput, hash: H256) {
- // TODO remove this
- todo!();
-
- // // TODO [3-spend-utxo]
- // UtxoStore::<T>::insert(hash, utxo);
+ // [3-spend-utxo]
+ UtxoStore::<T>::insert(hash, utxo);
// // further update 😉
}
/// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash.
fn get_simple_transaction(transaction: &Transaction) -> Vec<u8> {
- // TODO remove this
- todo!();
-
- // // TODO [3-spend-utxo]
- // let mut trx = transaction.clone();
- // for input in trx.inputs.iter_mut() {
- // input.sigscript = H512::zero();
- // }
+ // [3-spend-utxo]
+ let mut trx = transaction.clone();
+ for input in trx.inputs.iter_mut() {
+ input.sigscript = H512::zero();
+ }
- // trx.encode()
+ trx.encode()
}
/// Check transaction for validity, errors, & race conditions
@@ -251,100 +239,97 @@ pub mod pallet {
pub fn validate_transaction(
transaction: &Transaction,
) -> Result<ValidTransaction, &'static str> {
- // TODO remove this
- todo!();
-
- // TODO [3-spend-utxo]
- // // Check inputs and outputs are not empty
- // ensure!(!transaction.inputs.is_empty(), Error::<T>::EmptyInput);
- // ensure!(!transaction.outputs.is_empty(), Error::<T>::EmptyOutput);
-
- // // Check each input is used exactly once
- // {
- // let input_set: BTreeMap<_, ()> =
- // transaction.inputs.iter().map(|input| (input, ())).collect();
- // ensure!(
- // input_set.len() == transaction.inputs.len(),
- // Error::<T>::DuplicatedInput
- // );
- // }
- // {
- // let output_set: BTreeMap<_, ()> = transaction
- // .outputs
- // .iter()
- // .map(|output| (output, ()))
- // .collect();
- // ensure!(
- // output_set.len() == transaction.outputs.len(),
- // Error::<T>::DuplicatedOutput
- // );
- // }
-
- // let mut total_input: Value = 0;
- // let mut total_output: Value = 0;
- // let mut output_index: u64 = 0;
- // let simple_transaction = Self::get_simple_transaction(transaction);
-
- // // Variables sent to transaction pool
- // let mut missing_utxos = Vec::new();
- // let mut new_utxos = Vec::new();
- // let mut reward = 0;
-
- // for input in transaction.inputs.iter() {
- // // Check all inputs match to existing, unspent and unlocked outputs
- // if let Some(input_utxo) = UtxoStore::<T>::get(&input.outpoint) {
- // // Check provided signatures are valid
- // let is_valid_sig = sp_io::crypto::sr25519_verify(
- // &Signature::from_raw(*input.sigscript.as_fixed_bytes()),
- // &simple_transaction,
- // &Public::from_h256(input_utxo.pubkey),
- // );
- // ensure!(is_valid_sig, Error::<T>::InvalidSignature);
- // // Check sum of input values does not overflow
- // total_input = total_input
- // .checked_add(input_utxo.value)
- // .ok_or(Error::<T>::InputOverflow)?;
- // } else {
- // missing_utxos.push(input.outpoint.clone().as_fixed_bytes().to_vec());
- // }
- // }
-
- // // Check each output is defined exactly once and has nonzero value
- // for output in transaction.outputs.iter() {
- // ensure!(output.value > 0, Error::<T>::ZeroAmount);
- // let hash = BlakeTwo256::hash_of(&(&transaction.encode(), output_index));
- // output_index = output_index
- // .checked_add(1)
- // .ok_or(Error::<T>::MaximumTransactionDepth)?;
- // // Check new outputs do not collide with existing ones
- // ensure!(
- // !UtxoStore::<T>::contains_key(hash),
- // Error::<T>::DuplicatedOutput
- // );
- // // Check sum of output values does not overflow
- // total_output = total_output
- // .checked_add(output.value)
- // .ok_or(Error::<T>::OutputOverflow)?;
- // new_utxos.push(hash.as_fixed_bytes().to_vec());
- // }
-
- // // If no race condition, check the math
- // if missing_utxos.is_empty() {
- // // Check total output value must not exceed total input value
- // ensure!(total_input >= total_output, Error::<T>::OutputOverInput);
- // reward = total_input
- // .checked_sub(total_output)
- // .ok_or(Error::<T>::RewardOverflow)?;
- // }
-
- // // Returns transaction details
- // Ok(ValidTransaction {
- // requires: missing_utxos,
- // provides: new_utxos,
- // priority: reward as u64,
- // longevity: TransactionLongevity::max_value(),
- // propagate: true,
- // })
+ // [3-spend-utxo]
+ // Check inputs and outputs are not empty
+ ensure!(!transaction.inputs.is_empty(), Error::<T>::EmptyInput);
+ ensure!(!transaction.outputs.is_empty(), Error::<T>::EmptyOutput);
+
+ // Check each input is used exactly once
+ {
+ let input_set: BTreeMap<_, ()> =
+ transaction.inputs.iter().map(|input| (input, ())).collect();
+ ensure!(
+ input_set.len() == transaction.inputs.len(),
+ Error::<T>::DuplicatedInput
+ );
+ }
+ {
+ let output_set: BTreeMap<_, ()> = transaction
+ .outputs
+ .iter()
+ .map(|output| (output, ()))
+ .collect();
+ ensure!(
+ output_set.len() == transaction.outputs.len(),
+ Error::<T>::DuplicatedOutput
+ );
+ }
+
+ let mut total_input: Value = 0;
+ let mut total_output: Value = 0;
+ let mut output_index: u64 = 0;
+ let simple_transaction = Self::get_simple_transaction(transaction);
+
+ // Variables sent to transaction pool
+ let mut missing_utxos = Vec::new();
+ let mut new_utxos = Vec::new();
+ let mut reward = 0;
+
+ for input in transaction.inputs.iter() {
+ // Check all inputs match to existing, unspent and unlocked outputs
+ if let Some(input_utxo) = UtxoStore::<T>::get(&input.outpoint) {
+ // Check provided signatures are valid
+ let is_valid_sig = sp_io::crypto::sr25519_verify(
+ &Signature::from_raw(*input.sigscript.as_fixed_bytes()),
+ &simple_transaction,
+ &Public::from_h256(input_utxo.pubkey),
+ );
+ ensure!(is_valid_sig, Error::<T>::InvalidSignature);
+ // Check sum of input values does not overflow
+ total_input = total_input
+ .checked_add(input_utxo.value)
+ .ok_or(Error::<T>::InputOverflow)?;
+ } else {
+ missing_utxos.push(input.outpoint.clone().as_fixed_bytes().to_vec());
+ }
+ }
+
+ // Check each output is defined exactly once and has nonzero value
+ for output in transaction.outputs.iter() {
+ ensure!(output.value > 0, Error::<T>::ZeroAmount);
+ let hash = BlakeTwo256::hash_of(&(&transaction.encode(), output_index));
+ output_index = output_index
+ .checked_add(1)
+ .ok_or(Error::<T>::MaximumTransactionDepth)?;
+ // Check new outputs do not collide with existing ones
+ ensure!(
+ !UtxoStore::<T>::contains_key(hash),
+ Error::<T>::DuplicatedOutput
+ );
+ // Check sum of output values does not overflow
+ total_output = total_output
+ .checked_add(output.value)
+ .ok_or(Error::<T>::OutputOverflow)?;
+ new_utxos.push(hash.as_fixed_bytes().to_vec());
+ }
+
+ // If no race condition, check the math
+ if missing_utxos.is_empty() {
+ // Check total output value must not exceed total input value
+ ensure!(total_input >= total_output, Error::<T>::OutputOverInput);
+ reward = total_input
+ .checked_sub(total_output)
+ .ok_or(Error::<T>::RewardOverflow)?;
+ }
+
+ // Returns transaction details
+ Ok(ValidTransaction {
+ requires: missing_utxos,
+ provides: new_utxos,
+ priority: reward as u64,
+ longevity: TransactionLongevity::max_value(),
+ propagate: true,
+ })
}
}
}
\ No newline at end of file
Disperse Reward
[!Note] Resolve all TODO in
runtime/src/utxo.rs
to complete this step.
Reading Materials
The block reward is a combination of the block subsidy and all transaction fees paid by transactions in a specific block. When miners mine a block, they receive a block subsidy in the form of newly minted bitcoin. All transactions also include a fee, which miners collect. The block reward is the sum of these two amounts. As block subsidies are cut in half every four years, fees will slowly become a greater portion of the block reward. The term block reward and block subsidy are occasionally used interchangeably.
The block reward is paid out in the blockchain transaction of each block. This special transaction is the first transaction in every block, and it has no inputs.
So the problem in this step is that we've to distribute the block reward to the miners who successfully solve the puzzle to mine new blocks.
Before go to this tutorial, please read these documents to understand transaction lifecycle in Substrate:
-
Recap: Onchain/STF Hooks:
graph LR subgraph AfterTransactions direction LR OnIdle --> OnFinalize end subgraph OnChain direction LR Optional --> BeforeExtrinsics BeforeExtrinsics --> Inherents Inherents --> Poll Poll --> Transactions Transactions --> AfterTransactions end subgraph Optional OnRuntimeUpgrade end subgraph BeforeExtrinsics OnInitialize end subgraph Transactions Transaction1 --> UnsignedTransaction2 --> Transaction3 end subgraph Inherents Inherent1 --> Inherent2 end
Implement
To disperse block reward to block author, we'll use on_finalize
hook.
To implement the Hooks trait provides logic that is executed at the end of each block.
- Start with
#[pallet::hooks]
macro. - Implements the
Hooks
trait for the pallet. This trait is generic over theBlockNumberFor<T>
, which represents the type of block number defined in the runtime configuration. fn on_finalize(_n: BlockNumberFor<T>)
: This function is triggered automatically at the end of each block.
#![allow(unused)] fn main() { #[pallet::hooks] impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { fn on_finalize(_n: BlockNumberFor<T>) { match T::BlockAuthor::block_author() { // Block author did not provide key to claim reward None => Self::deposit_event(Event::RewardWasted), // Block author did provide key, so issue thir reward Some(author) => Self::disperse_reward(&author), } } } }
Implement logic of function disperse_reward
#![allow(unused)] fn main() { /// Redistribute combined reward value to block Author fn disperse_reward(author: &Public) { // take the cumulative reward in storage // plus issuance of blocknumber let reward = TotalReward::<T>::take() + T::Issuance::issuance(frame_system::Pallet::<T>::block_number()); // create new UTXO let utxo = TransactionOutput { value: reward, pubkey: H256::from_slice(author.as_slice()), }; // hash UTXO to get the key let hash = BlakeTwo256::hash_of(&( &utxo, frame_system::Pallet::<T>::block_number().saturated_into::<u64>(), )); // mutate storage Self::store_utxo(&utxo, hash); // emit event `RewardDistributed` Self::deposit_event(Event::RewardDistributed(reward, hash)); } }
Build the code
cargo build --release
Fantastic job completing a new step! 🌟 You're almost there. Let's move forward to next step.
#![allow(unused)] fn main() { // We make sure this pallet uses `no_std` for compiling to Wasm. #![cfg_attr(not(feature = "std"), no_std)] use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_core::{ sr25519::{Public, Signature}, ByteArray, H256, H512, }; use sp_runtime::traits::{BlakeTwo256, Hash, SaturatedConversion}; use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; use super::{block_author::BlockAuthor, issuance::Issuance}; pub use pallet::*; /// [2-data-structure] pub type Value = u128; /// [2-data-structure] /// Single transaction to be dispatched #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct Transaction { /// UTXOs to be used as inputs for current transaction pub inputs: Vec<TransactionInput>, /// UTXOs to be created as a result of current transaction dispatch pub outputs: Vec<TransactionOutput>, } /// [2-data-structure] /// Single transaction input that refers to one UTXO #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct TransactionInput { /// Reference to an UTXO to be spent pub outpoint: H256, /// Proof that transaction owner is authorized to spend referred UTXO & /// that the entire transaction is untampered pub sigscript: H512, } /// [2-data-structure] /// Single transaction output to create upon transaction dispatch #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct TransactionOutput { /// Value associated with this output pub value: Value, /// Public key associated with this output. In order to spend this output /// owner must provide a proof by hashing the whole `Transaction` and /// signing it with a corresponding private key. pub pubkey: H256, } #[frame_support::pallet(dev_mode)] pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use super::*; /// [2-data-structure] #[pallet::config] pub trait Config: frame_system::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. /// Read more: https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_runtime_types/index.html type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; /// A source to determine the block author type BlockAuthor: BlockAuthor; /// A source to determine the issuance portion of the block reward type Issuance: Issuance<BlockNumberFor<Self>, Value>; } #[pallet::pallet] pub struct Pallet<T>(_); /// [2-data-structure] /// Total reward value to be redistributed among authorities. /// It is accumulated from transactions during block execution /// and then dispersed to validators on block finalization. #[pallet::storage] #[pallet::getter(fn total_reward)] pub type TotalReward<T: Config> = StorageValue<_, Value, ValueQuery>; /// [2-data-structure] /// All valid unspent transaction outputs are stored in this map. /// Initial set of UTXO is populated from the list stored in genesis. /// We use the identity hasher here because the cryptographic hashing is /// done explicitly. /// Mapping from `BlakeTwo256::hash_of(transaction, index)` to `TransactionOutput` #[pallet::storage] #[pallet::getter(fn utxo_store)] pub type UtxoStore<T: Config> = StorageMap< Hasher = Identity, Key = H256, Value = TransactionOutput, QueryKind = OptionQuery, >; /// [2-data-structure] /// Pallets use events to inform users when important changes are made. #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config> { /// Dispatch transaction successful TransactionSuccess(Transaction), /// UTXO out processed TransactionOutputProcessed(H256), /// Reward distributed to `BlockAuthor` RewardDistributed(Value, H256), /// Faucet to `To` Faucet(Value, H256), /// No one get reward RewardWasted, } /// [2-data-structure] /// Errors inform users that something went wrong. #[pallet::error] pub enum Error<T> { /// Missing `Transaction` Input MissingInput, /// Reward overflow RewardOverflow, /// Maximum transaction depth MaximumTransactionDepth, /// Empty input EmptyInput, /// Empty output EmptyOutput, /// Each input must only be used once DuplicatedInput, /// Each output must be defined only once DuplicatedOutput, /// Input value is overflow InputOverflow, /// Output value is overflow OutputOverflow, /// Output spent must lte than Input spent OutputOverInput, /// Zero amount spent ZeroAmount, /// Invalid signature InvalidSignature, } /// TODO [4-dispersed-reward] // #[pallet::hooks] // impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { // fn on_finalize(_n: BlockNumberFor<T>) { // match T::BlockAuthor::block_author() { // // Block author did not provide key to claim reward // None => Self::deposit_event(Event::RewardWasted), // // Block author did provide key, so issue thir reward // Some(author) => Self::disperse_reward(&author), // } // } // } /// [2-data-structure] /// Define extrinsics / dispatchable function #[pallet::call] impl<T: Config> Pallet<T> { pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult { // [3-spend-utxo] // validate transaction let transaction_validity = Self::validate_transaction(&transaction)?; ensure!( transaction_validity.requires.is_empty(), Error::<T>::MissingInput ); // implement logic Self::do_spend(&transaction, transaction_validity.priority as Value)?; // emit event Self::deposit_event(Event::<T>::TransactionSuccess(transaction)); Ok(()) } } /// [2-data-structure] /// Define intrinsics impl<T: Config> Pallet<T> { /// Implement spend logic, update storage to reflect changes made by transaction /// Where each UTXO key is a hash of the entire transaction and its order in the `TransactionOutputs` vector fn do_spend(transaction: &Transaction, reward: Value) -> DispatchResult { // [3-spend-utxo] // Calculate new reward total. The rest of `total_input - total_output` will be used for block reward. let new_total = TotalReward::<T>::get() .checked_add(reward) .ok_or(Error::<T>::RewardOverflow)?; TotalReward::<T>::put(new_total); // Removing spent UTXOs for input in &transaction.inputs { UtxoStore::<T>::remove(input.outpoint); } let mut index: u64 = 0; for output in &transaction.outputs { let hash = BlakeTwo256::hash_of(&(&transaction.encode(), index)); // validated before, this is safe index = index .checked_add(1) .ok_or(Error::<T>::MaximumTransactionDepth) .unwrap(); Self::store_utxo(output, hash); Self::deposit_event(Event::TransactionOutputProcessed(hash)); } Ok(()) } /// Redistribute combined reward value to block Author fn disperse_reward(author: &Public) { // TODO remove this todo!(); // TODO [4-dispersed-reward] // take the rest of reward // plus issuance reward of current block number // let reward = TotalReward::<T>::take() // + T::Issuance::issuance(frame_system::Pallet::<T>::block_number()); // let utxo = TransactionOutput { // value: reward, // pubkey: H256::from_slice(author.as_slice()), // }; // let hash = BlakeTwo256::hash_of(&( // &utxo, // frame_system::Pallet::<T>::block_number().saturated_into::<u64>(), // )); // Self::store_utxo(&utxo, hash); // Self::deposit_event(Event::RewardDistributed(reward, hash)); } /// Mutate storage, insert / update new UTXOs fn store_utxo(utxo: &TransactionOutput, hash: H256) { // [3-spend-utxo] UtxoStore::<T>::insert(hash, utxo); // // further update 😉 } /// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash. fn get_simple_transaction(transaction: &Transaction) -> Vec<u8> { // [3-spend-utxo] let mut trx = transaction.clone(); for input in trx.inputs.iter_mut() { input.sigscript = H512::zero(); } trx.encode() } /// Check transaction for validity, errors, & race conditions /// Called by both transaction pool and runtime execution pub fn validate_transaction( transaction: &Transaction, ) -> Result<ValidTransaction, &'static str> { // [3-spend-utxo] // Check inputs and outputs are not empty ensure!(!transaction.inputs.is_empty(), Error::<T>::EmptyInput); ensure!(!transaction.outputs.is_empty(), Error::<T>::EmptyOutput); // Check each input is used exactly once { let input_set: BTreeMap<_, ()> = transaction.inputs.iter().map(|input| (input, ())).collect(); ensure!( input_set.len() == transaction.inputs.len(), Error::<T>::DuplicatedInput ); } { let output_set: BTreeMap<_, ()> = transaction .outputs .iter() .map(|output| (output, ())) .collect(); ensure!( output_set.len() == transaction.outputs.len(), Error::<T>::DuplicatedOutput ); } let mut total_input: Value = 0; let mut total_output: Value = 0; let mut output_index: u64 = 0; let simple_transaction = Self::get_simple_transaction(transaction); // Variables sent to transaction pool let mut missing_utxos = Vec::new(); let mut new_utxos = Vec::new(); let mut reward = 0; for input in transaction.inputs.iter() { // Check all inputs match to existing, unspent and unlocked outputs if let Some(input_utxo) = UtxoStore::<T>::get(&input.outpoint) { // Check provided signatures are valid let is_valid_sig = sp_io::crypto::sr25519_verify( &Signature::from_raw(*input.sigscript.as_fixed_bytes()), &simple_transaction, &Public::from_h256(input_utxo.pubkey), ); ensure!(is_valid_sig, Error::<T>::InvalidSignature); // Check sum of input values does not overflow total_input = total_input .checked_add(input_utxo.value) .ok_or(Error::<T>::InputOverflow)?; } else { missing_utxos.push(input.outpoint.clone().as_fixed_bytes().to_vec()); } } // Check each output is defined exactly once and has nonzero value for output in transaction.outputs.iter() { ensure!(output.value > 0, Error::<T>::ZeroAmount); let hash = BlakeTwo256::hash_of(&(&transaction.encode(), output_index)); output_index = output_index .checked_add(1) .ok_or(Error::<T>::MaximumTransactionDepth)?; // Check new outputs do not collide with existing ones ensure!( !UtxoStore::<T>::contains_key(hash), Error::<T>::DuplicatedOutput ); // Check sum of output values does not overflow total_output = total_output .checked_add(output.value) .ok_or(Error::<T>::OutputOverflow)?; new_utxos.push(hash.as_fixed_bytes().to_vec()); } // If no race condition, check the math if missing_utxos.is_empty() { // Check total output value must not exceed total input value ensure!(total_input >= total_output, Error::<T>::OutputOverInput); reward = total_input .checked_sub(total_output) .ok_or(Error::<T>::RewardOverflow)?; } // Returns transaction details Ok(ValidTransaction { requires: missing_utxos, provides: new_utxos, priority: reward as u64, longevity: TransactionLongevity::max_value(), propagate: true, }) } } } }
#![allow(unused)] fn main() { // We make sure this pallet uses `no_std` for compiling to Wasm. #![cfg_attr(not(feature = "std"), no_std)] use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_core::{ sr25519::{Public, Signature}, ByteArray, H256, H512, }; use sp_runtime::traits::{BlakeTwo256, Hash, SaturatedConversion}; use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; use super::{block_author::BlockAuthor, issuance::Issuance}; pub use pallet::*; /// [2-data-structure] pub type Value = u128; /// [2-data-structure] /// Single transaction to be dispatched #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct Transaction { /// UTXOs to be used as inputs for current transaction pub inputs: Vec<TransactionInput>, /// UTXOs to be created as a result of current transaction dispatch pub outputs: Vec<TransactionOutput>, } /// [2-data-structure] /// Single transaction input that refers to one UTXO #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct TransactionInput { /// Reference to an UTXO to be spent pub outpoint: H256, /// Proof that transaction owner is authorized to spend referred UTXO & /// that the entire transaction is untampered pub sigscript: H512, } /// [2-data-structure] /// Single transaction output to create upon transaction dispatch #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct TransactionOutput { /// Value associated with this output pub value: Value, /// Public key associated with this output. In order to spend this output /// owner must provide a proof by hashing the whole `Transaction` and /// signing it with a corresponding private key. pub pubkey: H256, } #[frame_support::pallet(dev_mode)] pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use super::*; /// [2-data-structure] #[pallet::config] pub trait Config: frame_system::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. /// Read more: https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_runtime_types/index.html type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; /// A source to determine the block author type BlockAuthor: BlockAuthor; /// A source to determine the issuance portion of the block reward type Issuance: Issuance<BlockNumberFor<Self>, Value>; } #[pallet::pallet] pub struct Pallet<T>(_); /// [2-data-structure] /// Total reward value to be redistributed among authorities. /// It is accumulated from transactions during block execution /// and then dispersed to validators on block finalization. #[pallet::storage] #[pallet::getter(fn total_reward)] pub type TotalReward<T: Config> = StorageValue<_, Value, ValueQuery>; /// [2-data-structure] /// All valid unspent transaction outputs are stored in this map. /// Initial set of UTXO is populated from the list stored in genesis. /// We use the identity hasher here because the cryptographic hashing is /// done explicitly. /// Mapping from `BlakeTwo256::hash_of(transaction, index)` to `TransactionOutput` #[pallet::storage] #[pallet::getter(fn utxo_store)] pub type UtxoStore<T: Config> = StorageMap< Hasher = Identity, Key = H256, Value = TransactionOutput, QueryKind = OptionQuery, >; /// [2-data-structure] /// Pallets use events to inform users when important changes are made. #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config> { /// Dispatch transaction successful TransactionSuccess(Transaction), /// UTXO out processed TransactionOutputProcessed(H256), /// Reward distributed to `BlockAuthor` RewardDistributed(Value, H256), /// Faucet to `To` Faucet(Value, H256), /// No one get reward RewardWasted, } /// [2-data-structure] /// Errors inform users that something went wrong. #[pallet::error] pub enum Error<T> { /// Missing `Transaction` Input MissingInput, /// Reward overflow RewardOverflow, /// Maximum transaction depth MaximumTransactionDepth, /// Empty input EmptyInput, /// Empty output EmptyOutput, /// Each input must only be used once DuplicatedInput, /// Each output must be defined only once DuplicatedOutput, /// Input value is overflow InputOverflow, /// Output value is overflow OutputOverflow, /// Output spent must lte than Input spent OutputOverInput, /// Zero amount spent ZeroAmount, /// Invalid signature InvalidSignature, } /// [4-dispersed-reward] #[pallet::hooks] impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { fn on_finalize(_n: BlockNumberFor<T>) { match T::BlockAuthor::block_author() { // Block author did not provide key to claim reward None => Self::deposit_event(Event::RewardWasted), // Block author did provide key, so issue thir reward Some(author) => Self::disperse_reward(&author), } } } /// [2-data-structure] /// Define extrinsics / dispatchable function #[pallet::call] impl<T: Config> Pallet<T> { pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult { // [3-spend-utxo] // validate transaction let transaction_validity = Self::validate_transaction(&transaction)?; ensure!( transaction_validity.requires.is_empty(), Error::<T>::MissingInput ); // implement logic Self::do_spend(&transaction, transaction_validity.priority as Value)?; // emit event Self::deposit_event(Event::<T>::TransactionSuccess(transaction)); Ok(()) } } /// [2-data-structure] /// Define intrinsics impl<T: Config> Pallet<T> { /// Implement spend logic, update storage to reflect changes made by transaction /// Where each UTXO key is a hash of the entire transaction and its order in the `TransactionOutputs` vector fn do_spend(transaction: &Transaction, reward: Value) -> DispatchResult { // [3-spend-utxo] // Calculate new reward total. The rest of `total_input - total_output` will be used for block reward. let new_total = TotalReward::<T>::get() .checked_add(reward) .ok_or(Error::<T>::RewardOverflow)?; TotalReward::<T>::put(new_total); // Removing spent UTXOs for input in &transaction.inputs { UtxoStore::<T>::remove(input.outpoint); } let mut index: u64 = 0; for output in &transaction.outputs { let hash = BlakeTwo256::hash_of(&(&transaction.encode(), index)); // validated before, this is safe index = index .checked_add(1) .ok_or(Error::<T>::MaximumTransactionDepth) .unwrap(); Self::store_utxo(output, hash); Self::deposit_event(Event::TransactionOutputProcessed(hash)); } Ok(()) } /// Redistribute combined reward value to block Author fn disperse_reward(author: &Public) { // [4-dispersed-reward] // take the rest of reward // plus issuance reward of current block number let reward = TotalReward::<T>::take() + T::Issuance::issuance(frame_system::Pallet::<T>::block_number()); let utxo = TransactionOutput { value: reward, pubkey: H256::from_slice(author.as_slice()), }; let hash = BlakeTwo256::hash_of(&( &utxo, frame_system::Pallet::<T>::block_number().saturated_into::<u64>(), )); Self::store_utxo(&utxo, hash); Self::deposit_event(Event::RewardDistributed(reward, hash)); } /// Mutate storage, insert / update new UTXOs fn store_utxo(utxo: &TransactionOutput, hash: H256) { // [3-spend-utxo] UtxoStore::<T>::insert(hash, utxo); // // further update 😉 } /// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash. fn get_simple_transaction(transaction: &Transaction) -> Vec<u8> { // [3-spend-utxo] let mut trx = transaction.clone(); for input in trx.inputs.iter_mut() { input.sigscript = H512::zero(); } trx.encode() } /// Check transaction for validity, errors, & race conditions /// Called by both transaction pool and runtime execution pub fn validate_transaction( transaction: &Transaction, ) -> Result<ValidTransaction, &'static str> { // [3-spend-utxo] // Check inputs and outputs are not empty ensure!(!transaction.inputs.is_empty(), Error::<T>::EmptyInput); ensure!(!transaction.outputs.is_empty(), Error::<T>::EmptyOutput); // Check each input is used exactly once { let input_set: BTreeMap<_, ()> = transaction.inputs.iter().map(|input| (input, ())).collect(); ensure!( input_set.len() == transaction.inputs.len(), Error::<T>::DuplicatedInput ); } { let output_set: BTreeMap<_, ()> = transaction .outputs .iter() .map(|output| (output, ())) .collect(); ensure!( output_set.len() == transaction.outputs.len(), Error::<T>::DuplicatedOutput ); } let mut total_input: Value = 0; let mut total_output: Value = 0; let mut output_index: u64 = 0; let simple_transaction = Self::get_simple_transaction(transaction); // Variables sent to transaction pool let mut missing_utxos = Vec::new(); let mut new_utxos = Vec::new(); let mut reward = 0; for input in transaction.inputs.iter() { // Check all inputs match to existing, unspent and unlocked outputs if let Some(input_utxo) = UtxoStore::<T>::get(&input.outpoint) { // Check provided signatures are valid let is_valid_sig = sp_io::crypto::sr25519_verify( &Signature::from_raw(*input.sigscript.as_fixed_bytes()), &simple_transaction, &Public::from_h256(input_utxo.pubkey), ); ensure!(is_valid_sig, Error::<T>::InvalidSignature); // Check sum of input values does not overflow total_input = total_input .checked_add(input_utxo.value) .ok_or(Error::<T>::InputOverflow)?; } else { missing_utxos.push(input.outpoint.clone().as_fixed_bytes().to_vec()); } } // Check each output is defined exactly once and has nonzero value for output in transaction.outputs.iter() { ensure!(output.value > 0, Error::<T>::ZeroAmount); let hash = BlakeTwo256::hash_of(&(&transaction.encode(), output_index)); output_index = output_index .checked_add(1) .ok_or(Error::<T>::MaximumTransactionDepth)?; // Check new outputs do not collide with existing ones ensure!( !UtxoStore::<T>::contains_key(hash), Error::<T>::DuplicatedOutput ); // Check sum of output values does not overflow total_output = total_output .checked_add(output.value) .ok_or(Error::<T>::OutputOverflow)?; new_utxos.push(hash.as_fixed_bytes().to_vec()); } // If no race condition, check the math if missing_utxos.is_empty() { // Check total output value must not exceed total input value ensure!(total_input >= total_output, Error::<T>::OutputOverInput); reward = total_input .checked_sub(total_output) .ok_or(Error::<T>::RewardOverflow)?; } // Returns transaction details Ok(ValidTransaction { requires: missing_utxos, provides: new_utxos, priority: reward as u64, longevity: TransactionLongevity::max_value(), propagate: true, }) } } } }
diff --git a/runtime/src/utxo.rs b/runtime/src/utxo.rs
index 00433df..1c51039 100644
--- a/runtime/src/utxo.rs
+++ b/runtime/src/utxo.rs
@@ -154,6 +154,18 @@ pub mod pallet {
InvalidSignature,
}
+ /// TODO [4-dispersed-reward]
+ // #[pallet::hooks]
+ // impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
+ // fn on_finalize(_n: BlockNumberFor<T>) {
+ // match T::BlockAuthor::block_author() {
+ // // Block author did not provide key to claim reward
+ // None => Self::deposit_event(Event::RewardWasted),
+ // // Block author did provide key, so issue thir reward
+ // Some(author) => Self::disperse_reward(&author),
+ // }
+ // }
+ // }
/// [2-data-structure]
/// Define extrinsics / dispatchable function
@@ -212,7 +224,28 @@ pub mod pallet {
/// Redistribute combined reward value to block Author
fn disperse_reward(author: &Public) {
+ // TODO remove this
todo!();
+
+ // TODO [4-dispersed-reward]
+
+ // take the rest of reward
+ // plus issuance reward of current block number
+ // let reward = TotalReward::<T>::take()
+ // + T::Issuance::issuance(frame_system::Pallet::<T>::block_number());
+
+ // let utxo = TransactionOutput {
+ // value: reward,
+ // pubkey: H256::from_slice(author.as_slice()),
+ // };
+
+ // let hash = BlakeTwo256::hash_of(&(
+ // &utxo,
+ // frame_system::Pallet::<T>::block_number().saturated_into::<u64>(),
+ // ));
+
+ // Self::store_utxo(&utxo, hash);
+ // Self::deposit_event(Event::RewardDistributed(reward, hash));
}
/// Mutate storage, insert / update new UTXOs
diff --git a/runtime/src/utxo.rs b/runtime/src/utxo.rs
index 1c51039..ed8ec6a 100644
--- a/runtime/src/utxo.rs
+++ b/runtime/src/utxo.rs
@@ -154,18 +154,18 @@ pub mod pallet {
InvalidSignature,
}
- /// TODO [4-dispersed-reward]
- // #[pallet::hooks]
- // impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
- // fn on_finalize(_n: BlockNumberFor<T>) {
- // match T::BlockAuthor::block_author() {
- // // Block author did not provide key to claim reward
- // None => Self::deposit_event(Event::RewardWasted),
- // // Block author did provide key, so issue thir reward
- // Some(author) => Self::disperse_reward(&author),
- // }
- // }
- // }
+ /// [4-dispersed-reward]
+ #[pallet::hooks]
+ impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
+ fn on_finalize(_n: BlockNumberFor<T>) {
+ match T::BlockAuthor::block_author() {
+ // Block author did not provide key to claim reward
+ None => Self::deposit_event(Event::RewardWasted),
+ // Block author did provide key, so issue thir reward
+ Some(author) => Self::disperse_reward(&author),
+ }
+ }
+ }
/// [2-data-structure]
/// Define extrinsics / dispatchable function
@@ -224,28 +224,25 @@ pub mod pallet {
/// Redistribute combined reward value to block Author
fn disperse_reward(author: &Public) {
- // TODO remove this
- todo!();
-
- // TODO [4-dispersed-reward]
+ // [4-dispersed-reward]
// take the rest of reward
// plus issuance reward of current block number
- // let reward = TotalReward::<T>::take()
- // + T::Issuance::issuance(frame_system::Pallet::<T>::block_number());
+ let reward = TotalReward::<T>::take()
+ + T::Issuance::issuance(frame_system::Pallet::<T>::block_number());
- // let utxo = TransactionOutput {
- // value: reward,
- // pubkey: H256::from_slice(author.as_slice()),
- // };
+ let utxo = TransactionOutput {
+ value: reward,
+ pubkey: H256::from_slice(author.as_slice()),
+ };
- // let hash = BlakeTwo256::hash_of(&(
- // &utxo,
- // frame_system::Pallet::<T>::block_number().saturated_into::<u64>(),
- // ));
+ let hash = BlakeTwo256::hash_of(&(
+ &utxo,
+ frame_system::Pallet::<T>::block_number().saturated_into::<u64>(),
+ ));
- // Self::store_utxo(&utxo, hash);
- // Self::deposit_event(Event::RewardDistributed(reward, hash));
+ Self::store_utxo(&utxo, hash);
+ Self::deposit_event(Event::RewardDistributed(reward, hash));
}
/// Mutate storage, insert / update new UTXOs
Runtime
[!Note] Resolve all TODO in
runtime/src/lib.rs
to complete this step.
Reading Materials
Implement
From the first step of building environment, I've already import pallet into the runtime for ya
#![allow(unused)] fn main() { /// UTXOs serve as the digital equivalent of change you receive after making a cash purchase pub mod utxo; }
Now, you just have to add the pallet UTXO to the runtime
+ impl utxo::Config for Runtime {
+ type RuntimeEvent = RuntimeEvent;
+ type BlockAuthor = BlockAuthor;
+ type Issuance = issuance::BitcoinHalving;
+ }
...
construct_runtime!(
pub struct Runtime {
System: frame_system,
Timestamp: pallet_timestamp,
Balances: pallet_balances,
TransactionPayment: pallet_transaction_payment,
Md5DifficultyAdjustment: difficulty::<Instance1>,
Sha3DifficultyAdjustment: difficulty::<Instance2>,
KeccakDifficultyAdjustment: difficulty::<Instance3>,
BlockAuthor: block_author,
+ Utxo: utxo,
}
);
...
impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime {
fn validate_transaction(
source: TransactionSource,
tx: <Block as BlockT>::Extrinsic,
block_hash: <Block as BlockT>::Hash,
) -> TransactionValidity {
+ // Extrinsics representing UTXO transaction need some special handling
+ if let Some(&utxo::Call::spend{ ref transaction }) = IsSubType::<<Utxo as Callable<Runtime>>::RuntimeCall>::is_sub_type(&tx.function)
+ {
+ match Utxo::validate_transaction(&transaction) {
+ // Transaction verification failed
+ Err(e) => {
+ sp_runtime::print(e);
+ return Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(1)));
+ }
+ // Race condition, or Transaction is good to go
+ Ok(tv) => { return Ok(tv); }
+ }
+ }
// Fall back to default logic for non UTXO-spending extrinsics
Executive::validate_transaction(source, tx, block_hash)
}
}
Build the code
cargo build --release
# start temporary local node in development environment
./target/release/academy-pow --dev --tmp
Direct to https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/explorer, and see block production. If you see after every a new block, there's also an event RewardDistributed
, your work are perfect!
Well done on completing! 🎉 You're on fire! Now, let's tackle next step.
#![allow(unused)] fn main() { //! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm. #![cfg_attr(not(feature = "std"), no_std)] // The construct runtime macro does a lot of recursion and requires us to increase the limit to 256. #![recursion_limit = "256"] // Make the WASM binary available. #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); pub use frame_support::{ construct_runtime, derive_impl, parameter_types, traits::{ Currency, EstimateNextNewSession, Imbalance, IsSubType, KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, ValidatorSet, VariantCountOf, }, weights::{ constants::{ BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, }, IdentityFee, Weight, }, Callable, StorageValue, }; use frame_support::{ genesis_builder_helper::{build_state, get_preset}, instances::{Instance1, Instance2, Instance3}, sp_runtime::Perquintill, traits::{ConstU128, ConstU32, ConstU8}, }; use multi_pow::SupportedHashes; pub use pallet_balances::Call as BalancesCall; pub use pallet_timestamp::Call as TimestampCall; use pallet_transaction_payment::{ConstFeeMultiplier, FungibleAdapter, Multiplier}; use parity_scale_codec::Decode; use sp_api::impl_runtime_apis; use sp_consensus_pow::POW_ENGINE_ID; use sp_core::OpaqueMetadata; // A few exports that help ease life for downstream crates. #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; use sp_runtime::{ create_runtime_str, generic, traits::{ AccountIdLookup, BlakeTwo256, Block as BlockT, Bounded, IdentifyAccount, One, Verify, }, transaction_validity::{ InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, }, ApplyExtrinsicResult, DigestItem, ExtrinsicInclusionMode, MultiSignature, }; pub use sp_runtime::{FixedPointNumber, Perbill, Permill}; use sp_std::prelude::*; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; /// An index to a block. pub type BlockNumber = u32; /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. pub type Signature = MultiSignature; /// Some way of identifying an account on the chain. We intentionally make it equivalent /// to the public key of our transaction signing scheme. pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId; /// The type for looking up accounts. We don't expect more than 4 billion of them, but you /// never know... pub type AccountIndex = u32; /// Balance of an account. pub type Balance = u128; /// Index of a transaction in the chain. pub type Nonce = u32; /// Index of a transaction in the chain. pub type Index = u32; /// A hash of some data used by the chain. pub type Hash = sp_core::H256; /// Consensus digest containing block author and supported hash algorithm. pub type PreDigest = (AccountId, SupportedHashes); /// The BlockAuthor trait in `./block_author.rs` pub mod block_author; // /// The Difficulty Adjustment Algorithm in `./difficulty.rs` pub mod difficulty; /// The total issuance and halving time pub mod issuance; /// UTXOs serve as the digital equivalent of change you receive after making a cash purchase pub mod utxo; /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know /// the specifics of the runtime. They can then be made to be agnostic over specific formats /// of data like extrinsics, allowing for them to continue syncing the network through upgrades /// to even the core data structures. pub mod opaque { pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; use super::*; /// Opaque block header type. pub type Header = generic::Header<BlockNumber, BlakeTwo256>; /// Opaque block type. pub type Block = generic::Block<Header, UncheckedExtrinsic>; /// Opaque block identifier type. pub type BlockId = generic::BlockId<Block>; } /// This runtime version. #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("academy-pow"), impl_name: create_runtime_str!("academy-pow"), authoring_version: 1, spec_version: 1, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, state_version: 1, }; /// The version information used to identify this runtime when compiled natively. #[cfg(feature = "std")] pub fn native_version() -> NativeVersion { NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } } const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); // native chain currency pub const TOKEN_SYMBOL: &str = "Unit"; pub const TOKEN_DECIMALS: u32 = 12; pub const TOKEN: u128 = 10u128.pow(TOKEN_DECIMALS); parameter_types! { pub const BlockHashCount: BlockNumber = 2400; pub const Version: RuntimeVersion = VERSION; /// We allow for 2 seconds of compute with a 6 second average block time. pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::with_sensible_defaults( Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), NORMAL_DISPATCH_RATIO, ); pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); pub const SS58Prefix: u8 = 42; } #[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)] impl frame_system::Config for Runtime { /// The basic call filter to use in dispatchable. type BaseCallFilter = frame_support::traits::Everything; /// Block & extrinsics weights: base values and limits. type BlockWeights = BlockWeights; /// The maximum length of a block (in bytes). type BlockLength = BlockLength; /// The identifier used to distinguish between accounts. type AccountId = AccountId; /// The aggregated dispatch type that is available for extrinsics. type RuntimeCall = RuntimeCall; /// The lookup mechanism to get account ID from whatever is passed in dispatchers. type Lookup = AccountIdLookup<AccountId, ()>; /// The type for hashing blocks and tries. type Hash = Hash; /// The hashing algorithm used. type Hashing = BlakeTwo256; /// The ubiquitous event type. type RuntimeEvent = RuntimeEvent; /// The ubiquitous origin type. type RuntimeOrigin = RuntimeOrigin; /// Maximum number of block number to block hash mappings to keep (oldest pruned first). type BlockHashCount = BlockHashCount; /// The weight of database operations that the runtime can invoke. type DbWeight = RocksDbWeight; /// Version of the runtime. type Version = Version; /// This type is being generated by the construct runtime macro. type PalletInfo = PalletInfo; /// What to do if a new account is created. type OnNewAccount = (); /// What to do if an account is fully reaped from the system. type OnKilledAccount = (); /// The data to be stored in an account. type AccountData = pallet_balances::AccountData<Balance>; /// Weight information for the extrinsics of this pallet. type SystemWeightInfo = (); /// This is used as an identifier of the chain. 42 is the generic substrate prefix. type SS58Prefix = SS58Prefix; /// The set code logic, just the default since we're not a parachain. type OnSetCode = (); type MaxConsumers = frame_support::traits::ConstU32<16>; type Nonce = Nonce; type Block = Block; } parameter_types! { pub const MinimumPeriod: u64 = 1000; } impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = (); type MinimumPeriod = MinimumPeriod; type WeightInfo = (); } impl pallet_balances::Config for Runtime { type MaxLocks = ConstU32<50>; type MaxReserves = (); type ReserveIdentifier = [u8; 8]; /// The type for recording an account's balance. type Balance = Balance; /// The ubiquitous event type. type RuntimeEvent = RuntimeEvent; type DustRemoval = (); type ExistentialDeposit = ConstU128<500>; type AccountStore = System; type WeightInfo = pallet_balances::weights::SubstrateWeight<Runtime>; type FreezeIdentifier = RuntimeFreezeReason; type MaxFreezes = VariantCountOf<RuntimeFreezeReason>; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; } parameter_types! { pub const TargetBlockTime: u128 = 5_000; // Setting min difficulty to damp factor per recommendation pub const DampFactor: u128 = 3; pub const ClampFactor: u128 = 2; pub const MaxDifficulty: u128 = u128::max_value(); } // Helper function to get the current blocks PoW algo from the predigest fn current_blocks_mining_algo() -> SupportedHashes { System::digest() .logs .iter() .find_map(|digest_item| match digest_item { DigestItem::PreRuntime(POW_ENGINE_ID, pre_digest) => PreDigest::decode(&mut &pre_digest[..]).map(|d| d.1).ok(), _ => None, }) .expect("There should be exactly one pow pre-digest item") } impl difficulty::Config<Instance1> for Runtime { type TimeProvider = Timestamp; type TargetBlockTime = TargetBlockTime; type DampFactor = DampFactor; type ClampFactor = ClampFactor; type MaxDifficulty = MaxDifficulty; type MinDifficulty = DampFactor; fn relevant_to_this_instance() -> bool { current_blocks_mining_algo() == SupportedHashes::Md5 } } impl difficulty::Config<Instance2> for Runtime { type TimeProvider = Timestamp; type TargetBlockTime = TargetBlockTime; type DampFactor = DampFactor; type ClampFactor = ClampFactor; type MaxDifficulty = MaxDifficulty; type MinDifficulty = DampFactor; fn relevant_to_this_instance() -> bool { current_blocks_mining_algo() == SupportedHashes::Sha3 } } impl difficulty::Config<Instance3> for Runtime { type TimeProvider = Timestamp; type TargetBlockTime = TargetBlockTime; type DampFactor = DampFactor; type ClampFactor = ClampFactor; type MaxDifficulty = MaxDifficulty; type MinDifficulty = DampFactor; fn relevant_to_this_instance() -> bool { current_blocks_mining_algo() == SupportedHashes::Keccak } } impl block_author::Config for Runtime { // Each block mined issues 50 new tokens to the miner fn on_author_set(author_account: Self::AccountId) { let issuance = 50 * TOKEN; let _ = Balances::deposit_creating(&author_account, issuance); } } parameter_types! { // This value increases the priority of `Operational` transactions by adding // a "virtual tip" that's equal to the `OperationalFeeMultiplier * final_fee`. // follows polkadot : https://github.com/paritytech/polkadot/blob/9ce5f7ef5abb1a4291454e8c9911b304d80679f9/runtime/polkadot/src/lib.rs#L369 pub const OperationalFeeMultiplier: u8 = 5; // We expect that on average 25% of the normal capacity will be occupied with normal txs. pub const TargetSaturationLevel: Perquintill = Perquintill::from_percent(25); // During 20 blocks the fee may not change more than by 100%. This, together with the // `TargetSaturationLevel` value, results in variability ~0.067. For the corresponding // formulas please refer to Substrate code at `frame/transaction-payment/src/lib.rs`. pub FeeVariability: Multiplier = Multiplier::saturating_from_rational(67, 1000); // Fee should never be lower than the computational cost. pub MinimumMultiplier: Multiplier = Multiplier::one(); pub MaximumMultiplier: Multiplier = Bounded::max_value(); } parameter_types! { pub FeeMultiplier: Multiplier = Multiplier::one(); } impl pallet_transaction_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = FungibleAdapter<Balances, ()>; type OperationalFeeMultiplier = ConstU8<5>; type WeightToFee = IdentityFee<Balance>; type LengthToFee = IdentityFee<Balance>; type FeeMultiplierUpdate = ConstFeeMultiplier<FeeMultiplier>; } // TODO [5-runtime] // impl utxo::Config for Runtime { // type RuntimeEvent = RuntimeEvent; // type BlockAuthor = BlockAuthor; // type Issuance = issuance::BitcoinHalving; // } construct_runtime!( pub struct Runtime { System: frame_system, Timestamp: pallet_timestamp, Balances: pallet_balances, TransactionPayment: pallet_transaction_payment, Md5DifficultyAdjustment: difficulty::<Instance1>, Sha3DifficultyAdjustment: difficulty::<Instance2>, KeccakDifficultyAdjustment: difficulty::<Instance3>, BlockAuthor: block_author, // TODO [5-runtime] // Utxo: utxo, } ); /// The address format for describing accounts. pub type Address = sp_runtime::MultiAddress<AccountId, ()>; /// Block header type as expected by this runtime. pub type Header = generic::Header<BlockNumber, BlakeTwo256>; /// Block type as expected by this runtime. pub type Block = generic::Block<Header, UncheckedExtrinsic>; /// A Block signed with a Justification pub type SignedBlock = generic::SignedBlock<Block>; /// The SignedExtension to the basic transaction logic. pub type SignedExtra = ( frame_system::CheckNonZeroSender<Runtime>, frame_system::CheckSpecVersion<Runtime>, frame_system::CheckTxVersion<Runtime>, frame_system::CheckGenesis<Runtime>, frame_system::CheckEra<Runtime>, frame_system::CheckNonce<Runtime>, frame_system::CheckWeight<Runtime>, pallet_transaction_payment::ChargeTransactionPayment<Runtime>, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, Block, frame_system::ChainContext<Runtime>, Runtime, AllPalletsWithSystem, >; impl_runtime_apis! { impl sp_api::Core<Block> for Runtime { fn version() -> RuntimeVersion { VERSION } fn execute_block(block: Block) { Executive::execute_block(block) } fn initialize_block(header: &<Block as BlockT>::Header) -> ExtrinsicInclusionMode { Executive::initialize_block(header) } } impl sp_api::Metadata<Block> for Runtime { fn metadata() -> OpaqueMetadata { OpaqueMetadata::new(Runtime::metadata().into()) } fn metadata_at_version(version: u32) -> Option<OpaqueMetadata> { Runtime::metadata_at_version(version) } fn metadata_versions() -> sp_std::vec::Vec<u32> { Runtime::metadata_versions() } } impl sp_block_builder::BlockBuilder<Block> for Runtime { fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult { Executive::apply_extrinsic(extrinsic) } fn finalize_block() -> <Block as BlockT>::Header { Executive::finalize_block() } fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> { data.create_extrinsics() } fn check_inherents( block: Block, data: sp_inherents::InherentData, ) -> sp_inherents::CheckInherentsResult { data.check_extrinsics(&block) } } impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime { fn validate_transaction( source: TransactionSource, tx: <Block as BlockT>::Extrinsic, block_hash: <Block as BlockT>::Hash, ) -> TransactionValidity { // TODO [5-runtime] // // Extrinsics representing UTXO transaction need some special handling // if let Some(&utxo::Call::spend{ ref transaction }) = IsSubType::<<Utxo as Callable<Runtime>>::RuntimeCall>::is_sub_type(&tx.function) // { // match Utxo::validate_transaction(&transaction) { // // Transaction verification failed // Err(e) => { // sp_runtime::print(e); // return Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(1))); // } // // Race condition, or Transaction is good to go // Ok(tv) => { return Ok(tv); } // } // } // Fall back to default logic for non UTXO-spending extrinsics Executive::validate_transaction(source, tx, block_hash) } } impl sp_offchain::OffchainWorkerApi<Block> for Runtime { fn offchain_worker(header: &<Block as BlockT>::Header) { Executive::offchain_worker(header) } } impl sp_session::SessionKeys<Block> for Runtime { fn generate_session_keys(_seed: Option<Vec<u8>>) -> Vec<u8> { Vec::new() } fn decode_session_keys( _encoded: Vec<u8>, ) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> { None } } impl sp_consensus_pow::DifficultyApi<Block, multi_pow::Threshold> for Runtime { fn difficulty() -> multi_pow::Threshold { multi_pow::Threshold { md5: Md5DifficultyAdjustment::difficulty(), sha3: Sha3DifficultyAdjustment::difficulty(), keccak: KeccakDifficultyAdjustment::difficulty(), } } } impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime { fn account_nonce(account: AccountId) -> Index { System::account_nonce(account) } } impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime { fn query_info( uxt: <Block as BlockT>::Extrinsic, len: u32, ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> { TransactionPayment::query_info(uxt, len) } fn query_fee_details( uxt: <Block as BlockT>::Extrinsic, len: u32, ) -> pallet_transaction_payment::FeeDetails<Balance> { TransactionPayment::query_fee_details(uxt, len) } fn query_weight_to_fee(weight: Weight) -> Balance { TransactionPayment::weight_to_fee(weight) } fn query_length_to_fee(length: u32) -> Balance { TransactionPayment::length_to_fee(length) } } impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi<Block, Balance, RuntimeCall> for Runtime { fn query_call_info( call: RuntimeCall, len: u32, ) -> pallet_transaction_payment::RuntimeDispatchInfo<Balance> { TransactionPayment::query_call_info(call, len) } fn query_call_fee_details( call: RuntimeCall, len: u32, ) -> pallet_transaction_payment::FeeDetails<Balance> { TransactionPayment::query_call_fee_details(call, len) } fn query_weight_to_fee(weight: Weight) -> Balance { TransactionPayment::weight_to_fee(weight) } fn query_length_to_fee(length: u32) -> Balance { TransactionPayment::length_to_fee(length) } } impl sp_genesis_builder::GenesisBuilder<Block> for Runtime { fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result { build_state::<RuntimeGenesisConfig>(config) } fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> { get_preset::<RuntimeGenesisConfig>(id, |_| None) } fn preset_names() -> Vec<sp_genesis_builder::PresetId> { Default::default() } } } }
#![allow(unused)] fn main() { //! The Substrate Node Template runtime. This can be compiled with `#[no_std]`, ready for Wasm. #![cfg_attr(not(feature = "std"), no_std)] // The construct runtime macro does a lot of recursion and requires us to increase the limit to 256. #![recursion_limit = "256"] // Make the WASM binary available. #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); pub use frame_support::{ construct_runtime, derive_impl, parameter_types, traits::{ Currency, EstimateNextNewSession, Imbalance, IsSubType, KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, ValidatorSet, VariantCountOf, }, weights::{ constants::{ BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND, }, IdentityFee, Weight, }, Callable, StorageValue, }; use frame_support::{ genesis_builder_helper::{build_state, get_preset}, instances::{Instance1, Instance2, Instance3}, sp_runtime::Perquintill, traits::{ConstU128, ConstU32, ConstU8}, }; use multi_pow::SupportedHashes; pub use pallet_balances::Call as BalancesCall; pub use pallet_timestamp::Call as TimestampCall; use pallet_transaction_payment::{ConstFeeMultiplier, FungibleAdapter, Multiplier}; use parity_scale_codec::Decode; use sp_api::impl_runtime_apis; use sp_consensus_pow::POW_ENGINE_ID; use sp_core::OpaqueMetadata; // A few exports that help ease life for downstream crates. #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; use sp_runtime::{ create_runtime_str, generic, traits::{ AccountIdLookup, BlakeTwo256, Block as BlockT, Bounded, IdentifyAccount, One, Verify, }, transaction_validity::{ InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, }, ApplyExtrinsicResult, DigestItem, ExtrinsicInclusionMode, MultiSignature, }; pub use sp_runtime::{FixedPointNumber, Perbill, Permill}; use sp_std::prelude::*; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; /// An index to a block. pub type BlockNumber = u32; /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. pub type Signature = MultiSignature; /// Some way of identifying an account on the chain. We intentionally make it equivalent /// to the public key of our transaction signing scheme. pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId; /// The type for looking up accounts. We don't expect more than 4 billion of them, but you /// never know... pub type AccountIndex = u32; /// Balance of an account. pub type Balance = u128; /// Index of a transaction in the chain. pub type Nonce = u32; /// Index of a transaction in the chain. pub type Index = u32; /// A hash of some data used by the chain. pub type Hash = sp_core::H256; /// Consensus digest containing block author and supported hash algorithm. pub type PreDigest = (AccountId, SupportedHashes); /// The BlockAuthor trait in `./block_author.rs` pub mod block_author; // /// The Difficulty Adjustment Algorithm in `./difficulty.rs` pub mod difficulty; /// The total issuance and halving time pub mod issuance; /// UTXOs serve as the digital equivalent of change you receive after making a cash purchase pub mod utxo; /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know /// the specifics of the runtime. They can then be made to be agnostic over specific formats /// of data like extrinsics, allowing for them to continue syncing the network through upgrades /// to even the core data structures. pub mod opaque { pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; use super::*; /// Opaque block header type. pub type Header = generic::Header<BlockNumber, BlakeTwo256>; /// Opaque block type. pub type Block = generic::Block<Header, UncheckedExtrinsic>; /// Opaque block identifier type. pub type BlockId = generic::BlockId<Block>; } /// This runtime version. #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("academy-pow"), impl_name: create_runtime_str!("academy-pow"), authoring_version: 1, spec_version: 1, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, state_version: 1, }; /// The version information used to identify this runtime when compiled natively. #[cfg(feature = "std")] pub fn native_version() -> NativeVersion { NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } } const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); // native chain currency pub const TOKEN_SYMBOL: &str = "Unit"; pub const TOKEN_DECIMALS: u32 = 12; pub const TOKEN: u128 = 10u128.pow(TOKEN_DECIMALS); parameter_types! { pub const BlockHashCount: BlockNumber = 2400; pub const Version: RuntimeVersion = VERSION; /// We allow for 2 seconds of compute with a 6 second average block time. pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::with_sensible_defaults( Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), NORMAL_DISPATCH_RATIO, ); pub BlockLength: frame_system::limits::BlockLength = frame_system::limits::BlockLength ::max_with_normal_ratio(5 * 1024 * 1024, NORMAL_DISPATCH_RATIO); pub const SS58Prefix: u8 = 42; } #[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)] impl frame_system::Config for Runtime { /// The basic call filter to use in dispatchable. type BaseCallFilter = frame_support::traits::Everything; /// Block & extrinsics weights: base values and limits. type BlockWeights = BlockWeights; /// The maximum length of a block (in bytes). type BlockLength = BlockLength; /// The identifier used to distinguish between accounts. type AccountId = AccountId; /// The aggregated dispatch type that is available for extrinsics. type RuntimeCall = RuntimeCall; /// The lookup mechanism to get account ID from whatever is passed in dispatchers. type Lookup = AccountIdLookup<AccountId, ()>; /// The type for hashing blocks and tries. type Hash = Hash; /// The hashing algorithm used. type Hashing = BlakeTwo256; /// The ubiquitous event type. type RuntimeEvent = RuntimeEvent; /// The ubiquitous origin type. type RuntimeOrigin = RuntimeOrigin; /// Maximum number of block number to block hash mappings to keep (oldest pruned first). type BlockHashCount = BlockHashCount; /// The weight of database operations that the runtime can invoke. type DbWeight = RocksDbWeight; /// Version of the runtime. type Version = Version; /// This type is being generated by the construct runtime macro. type PalletInfo = PalletInfo; /// What to do if a new account is created. type OnNewAccount = (); /// What to do if an account is fully reaped from the system. type OnKilledAccount = (); /// The data to be stored in an account. type AccountData = pallet_balances::AccountData<Balance>; /// Weight information for the extrinsics of this pallet. type SystemWeightInfo = (); /// This is used as an identifier of the chain. 42 is the generic substrate prefix. type SS58Prefix = SS58Prefix; /// The set code logic, just the default since we're not a parachain. type OnSetCode = (); type MaxConsumers = frame_support::traits::ConstU32<16>; type Nonce = Nonce; type Block = Block; } parameter_types! { pub const MinimumPeriod: u64 = 1000; } impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; type OnTimestampSet = (); type MinimumPeriod = MinimumPeriod; type WeightInfo = (); } impl pallet_balances::Config for Runtime { type MaxLocks = ConstU32<50>; type MaxReserves = (); type ReserveIdentifier = [u8; 8]; /// The type for recording an account's balance. type Balance = Balance; /// The ubiquitous event type. type RuntimeEvent = RuntimeEvent; type DustRemoval = (); type ExistentialDeposit = ConstU128<500>; type AccountStore = System; type WeightInfo = pallet_balances::weights::SubstrateWeight<Runtime>; type FreezeIdentifier = RuntimeFreezeReason; type MaxFreezes = VariantCountOf<RuntimeFreezeReason>; type RuntimeHoldReason = RuntimeHoldReason; type RuntimeFreezeReason = RuntimeFreezeReason; } parameter_types! { pub const TargetBlockTime: u128 = 5_000; // Setting min difficulty to damp factor per recommendation pub const DampFactor: u128 = 3; pub const ClampFactor: u128 = 2; pub const MaxDifficulty: u128 = u128::max_value(); } // Helper function to get the current blocks PoW algo from the predigest fn current_blocks_mining_algo() -> SupportedHashes { System::digest() .logs .iter() .find_map(|digest_item| match digest_item { DigestItem::PreRuntime(POW_ENGINE_ID, pre_digest) => PreDigest::decode(&mut &pre_digest[..]).map(|d| d.1).ok(), _ => None, }) .expect("There should be exactly one pow pre-digest item") } impl difficulty::Config<Instance1> for Runtime { type TimeProvider = Timestamp; type TargetBlockTime = TargetBlockTime; type DampFactor = DampFactor; type ClampFactor = ClampFactor; type MaxDifficulty = MaxDifficulty; type MinDifficulty = DampFactor; fn relevant_to_this_instance() -> bool { current_blocks_mining_algo() == SupportedHashes::Md5 } } impl difficulty::Config<Instance2> for Runtime { type TimeProvider = Timestamp; type TargetBlockTime = TargetBlockTime; type DampFactor = DampFactor; type ClampFactor = ClampFactor; type MaxDifficulty = MaxDifficulty; type MinDifficulty = DampFactor; fn relevant_to_this_instance() -> bool { current_blocks_mining_algo() == SupportedHashes::Sha3 } } impl difficulty::Config<Instance3> for Runtime { type TimeProvider = Timestamp; type TargetBlockTime = TargetBlockTime; type DampFactor = DampFactor; type ClampFactor = ClampFactor; type MaxDifficulty = MaxDifficulty; type MinDifficulty = DampFactor; fn relevant_to_this_instance() -> bool { current_blocks_mining_algo() == SupportedHashes::Keccak } } impl block_author::Config for Runtime { // Each block mined issues 50 new tokens to the miner fn on_author_set(author_account: Self::AccountId) { let issuance = 50 * TOKEN; let _ = Balances::deposit_creating(&author_account, issuance); } } parameter_types! { // This value increases the priority of `Operational` transactions by adding // a "virtual tip" that's equal to the `OperationalFeeMultiplier * final_fee`. // follows polkadot : https://github.com/paritytech/polkadot/blob/9ce5f7ef5abb1a4291454e8c9911b304d80679f9/runtime/polkadot/src/lib.rs#L369 pub const OperationalFeeMultiplier: u8 = 5; // We expect that on average 25% of the normal capacity will be occupied with normal txs. pub const TargetSaturationLevel: Perquintill = Perquintill::from_percent(25); // During 20 blocks the fee may not change more than by 100%. This, together with the // `TargetSaturationLevel` value, results in variability ~0.067. For the corresponding // formulas please refer to Substrate code at `frame/transaction-payment/src/lib.rs`. pub FeeVariability: Multiplier = Multiplier::saturating_from_rational(67, 1000); // Fee should never be lower than the computational cost. pub MinimumMultiplier: Multiplier = Multiplier::one(); pub MaximumMultiplier: Multiplier = Bounded::max_value(); } parameter_types! { pub FeeMultiplier: Multiplier = Multiplier::one(); } impl pallet_transaction_payment::Config for Runtime { type RuntimeEvent = RuntimeEvent; type OnChargeTransaction = FungibleAdapter<Balances, ()>; type OperationalFeeMultiplier = ConstU8<5>; type WeightToFee = IdentityFee<Balance>; type LengthToFee = IdentityFee<Balance>; type FeeMultiplierUpdate = ConstFeeMultiplier<FeeMultiplier>; } // [5-runtime] impl utxo::Config for Runtime { type RuntimeEvent = RuntimeEvent; type BlockAuthor = BlockAuthor; type Issuance = issuance::BitcoinHalving; } construct_runtime!( pub struct Runtime { System: frame_system, Timestamp: pallet_timestamp, Balances: pallet_balances, TransactionPayment: pallet_transaction_payment, Md5DifficultyAdjustment: difficulty::<Instance1>, Sha3DifficultyAdjustment: difficulty::<Instance2>, KeccakDifficultyAdjustment: difficulty::<Instance3>, BlockAuthor: block_author, // [5-runtime] Utxo: utxo, } ); /// The address format for describing accounts. pub type Address = sp_runtime::MultiAddress<AccountId, ()>; /// Block header type as expected by this runtime. pub type Header = generic::Header<BlockNumber, BlakeTwo256>; /// Block type as expected by this runtime. pub type Block = generic::Block<Header, UncheckedExtrinsic>; /// A Block signed with a Justification pub type SignedBlock = generic::SignedBlock<Block>; /// The SignedExtension to the basic transaction logic. pub type SignedExtra = ( frame_system::CheckNonZeroSender<Runtime>, frame_system::CheckSpecVersion<Runtime>, frame_system::CheckTxVersion<Runtime>, frame_system::CheckGenesis<Runtime>, frame_system::CheckEra<Runtime>, frame_system::CheckNonce<Runtime>, frame_system::CheckWeight<Runtime>, pallet_transaction_payment::ChargeTransactionPayment<Runtime>, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, RuntimeCall, Signature, SignedExtra>; /// Executive: handles dispatch to the various modules. pub type Executive = frame_executive::Executive< Runtime, Block, frame_system::ChainContext<Runtime>, Runtime, AllPalletsWithSystem, >; impl_runtime_apis! { impl sp_api::Core<Block> for Runtime { fn version() -> RuntimeVersion { VERSION } fn execute_block(block: Block) { Executive::execute_block(block) } fn initialize_block(header: &<Block as BlockT>::Header) -> ExtrinsicInclusionMode { Executive::initialize_block(header) } } impl sp_api::Metadata<Block> for Runtime { fn metadata() -> OpaqueMetadata { OpaqueMetadata::new(Runtime::metadata().into()) } fn metadata_at_version(version: u32) -> Option<OpaqueMetadata> { Runtime::metadata_at_version(version) } fn metadata_versions() -> sp_std::vec::Vec<u32> { Runtime::metadata_versions() } } impl sp_block_builder::BlockBuilder<Block> for Runtime { fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyExtrinsicResult { Executive::apply_extrinsic(extrinsic) } fn finalize_block() -> <Block as BlockT>::Header { Executive::finalize_block() } fn inherent_extrinsics(data: sp_inherents::InherentData) -> Vec<<Block as BlockT>::Extrinsic> { data.create_extrinsics() } fn check_inherents( block: Block, data: sp_inherents::InherentData, ) -> sp_inherents::CheckInherentsResult { data.check_extrinsics(&block) } } impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime { fn validate_transaction( source: TransactionSource, tx: <Block as BlockT>::Extrinsic, block_hash: <Block as BlockT>::Hash, ) -> TransactionValidity { // [5-runtime] // Extrinsics representing UTXO transaction need some special handling if let Some(&utxo::Call::spend{ ref transaction }) = IsSubType::<<Utxo as Callable<Runtime>>::RuntimeCall>::is_sub_type(&tx.function) { match Utxo::validate_transaction(&transaction) { // Transaction verification failed Err(e) => { sp_runtime::print(e); return Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(1))); } // Race condition, or Transaction is good to go Ok(tv) => { return Ok(tv); } } } // Fall back to default logic for non UTXO-spending extrinsics Executive::validate_transaction(source, tx, block_hash) } } impl sp_offchain::OffchainWorkerApi<Block> for Runtime { fn offchain_worker(header: &<Block as BlockT>::Header) { Executive::offchain_worker(header) } } impl sp_session::SessionKeys<Block> for Runtime { fn generate_session_keys(_seed: Option<Vec<u8>>) -> Vec<u8> { Vec::new() } fn decode_session_keys( _encoded: Vec<u8>, ) -> Option<Vec<(Vec<u8>, sp_core::crypto::KeyTypeId)>> { None } } impl sp_consensus_pow::DifficultyApi<Block, multi_pow::Threshold> for Runtime { fn difficulty() -> multi_pow::Threshold { multi_pow::Threshold { md5: Md5DifficultyAdjustment::difficulty(), sha3: Sha3DifficultyAdjustment::difficulty(), keccak: KeccakDifficultyAdjustment::difficulty(), } } } impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Index> for Runtime { fn account_nonce(account: AccountId) -> Index { System::account_nonce(account) } } impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi<Block, Balance> for Runtime { fn query_info( uxt: <Block as BlockT>::Extrinsic, len: u32, ) -> pallet_transaction_payment_rpc_runtime_api::RuntimeDispatchInfo<Balance> { TransactionPayment::query_info(uxt, len) } fn query_fee_details( uxt: <Block as BlockT>::Extrinsic, len: u32, ) -> pallet_transaction_payment::FeeDetails<Balance> { TransactionPayment::query_fee_details(uxt, len) } fn query_weight_to_fee(weight: Weight) -> Balance { TransactionPayment::weight_to_fee(weight) } fn query_length_to_fee(length: u32) -> Balance { TransactionPayment::length_to_fee(length) } } impl pallet_transaction_payment_rpc_runtime_api::TransactionPaymentCallApi<Block, Balance, RuntimeCall> for Runtime { fn query_call_info( call: RuntimeCall, len: u32, ) -> pallet_transaction_payment::RuntimeDispatchInfo<Balance> { TransactionPayment::query_call_info(call, len) } fn query_call_fee_details( call: RuntimeCall, len: u32, ) -> pallet_transaction_payment::FeeDetails<Balance> { TransactionPayment::query_call_fee_details(call, len) } fn query_weight_to_fee(weight: Weight) -> Balance { TransactionPayment::weight_to_fee(weight) } fn query_length_to_fee(length: u32) -> Balance { TransactionPayment::length_to_fee(length) } } impl sp_genesis_builder::GenesisBuilder<Block> for Runtime { fn build_state(config: Vec<u8>) -> sp_genesis_builder::Result { build_state::<RuntimeGenesisConfig>(config) } fn get_preset(id: &Option<sp_genesis_builder::PresetId>) -> Option<Vec<u8>> { get_preset::<RuntimeGenesisConfig>(id, |_| None) } fn preset_names() -> Vec<sp_genesis_builder::PresetId> { Default::default() } } } }
diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
index b724b7e..258420d 100644
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -325,6 +325,15 @@ impl pallet_transaction_payment::Config for Runtime {
type FeeMultiplierUpdate = ConstFeeMultiplier<FeeMultiplier>;
}
+
+// TODO [5-runtime]
+// impl utxo::Config for Runtime {
+// type RuntimeEvent = RuntimeEvent;
+// type BlockAuthor = BlockAuthor;
+// type Issuance = issuance::BitcoinHalving;
+// }
+
+
construct_runtime!(
pub struct Runtime {
System: frame_system,
@@ -335,6 +344,8 @@ construct_runtime!(
Sha3DifficultyAdjustment: difficulty::<Instance2>,
KeccakDifficultyAdjustment: difficulty::<Instance3>,
BlockAuthor: block_author,
+ // TODO [5-runtime]
+ // Utxo: utxo,
}
);
@@ -425,6 +436,23 @@ impl_runtime_apis! {
tx: <Block as BlockT>::Extrinsic,
block_hash: <Block as BlockT>::Hash,
) -> TransactionValidity {
+ // TODO [5-runtime]
+
+ // // Extrinsics representing UTXO transaction need some special handling
+ // if let Some(&utxo::Call::spend{ ref transaction }) = IsSubType::<<Utxo as Callable<Runtime>>::RuntimeCall>::is_sub_type(&tx.function)
+ // {
+ // match Utxo::validate_transaction(&transaction) {
+ // // Transaction verification failed
+ // Err(e) => {
+ // sp_runtime::print(e);
+ // return Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(1)));
+ // }
+ // // Race condition, or Transaction is good to go
+ // Ok(tv) => { return Ok(tv); }
+ // }
+ // }
+
+ // Fall back to default logic for non UTXO-spending extrinsics
Executive::validate_transaction(source, tx, block_hash)
}
}
diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
index 258420d..75498bf 100644
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -326,12 +326,12 @@ impl pallet_transaction_payment::Config for Runtime {
}
-// TODO [5-runtime]
-// impl utxo::Config for Runtime {
-// type RuntimeEvent = RuntimeEvent;
-// type BlockAuthor = BlockAuthor;
-// type Issuance = issuance::BitcoinHalving;
-// }
+// [5-runtime]
+impl utxo::Config for Runtime {
+ type RuntimeEvent = RuntimeEvent;
+ type BlockAuthor = BlockAuthor;
+ type Issuance = issuance::BitcoinHalving;
+}
construct_runtime!(
@@ -344,8 +344,8 @@ construct_runtime!(
Sha3DifficultyAdjustment: difficulty::<Instance2>,
KeccakDifficultyAdjustment: difficulty::<Instance3>,
BlockAuthor: block_author,
- // TODO [5-runtime]
- // Utxo: utxo,
+ // [5-runtime]
+ Utxo: utxo,
}
);
@@ -436,21 +436,21 @@ impl_runtime_apis! {
tx: <Block as BlockT>::Extrinsic,
block_hash: <Block as BlockT>::Hash,
) -> TransactionValidity {
- // TODO [5-runtime]
-
- // // Extrinsics representing UTXO transaction need some special handling
- // if let Some(&utxo::Call::spend{ ref transaction }) = IsSubType::<<Utxo as Callable<Runtime>>::RuntimeCall>::is_sub_type(&tx.function)
- // {
- // match Utxo::validate_transaction(&transaction) {
- // // Transaction verification failed
- // Err(e) => {
- // sp_runtime::print(e);
- // return Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(1)));
- // }
- // // Race condition, or Transaction is good to go
- // Ok(tv) => { return Ok(tv); }
- // }
- // }
+ // [5-runtime]
+
+ // Extrinsics representing UTXO transaction need some special handling
+ if let Some(&utxo::Call::spend{ ref transaction }) = IsSubType::<<Utxo as Callable<Runtime>>::RuntimeCall>::is_sub_type(&tx.function)
+ {
+ match Utxo::validate_transaction(&transaction) {
+ // Transaction verification failed
+ Err(e) => {
+ sp_runtime::print(e);
+ return Err(TransactionValidityError::Invalid(InvalidTransaction::Custom(1)));
+ }
+ // Race condition, or Transaction is good to go
+ Ok(tv) => { return Ok(tv); }
+ }
+ }
// Fall back to default logic for non UTXO-spending extrinsics
Executive::validate_transaction(source, tx, block_hash)
Genesis Builder
[!Note] Resolve all TODO in
runtime/src/utxo.rs
andruntime/src/chain_spec.rs
to complete this step.
Reading Materials
We've almost complete the UTXO pallet. However we have no balance to test it, that's quite ridiculous 🫤.
In this tutorial, I'll show you how to add gensis data for pallet, have initial UTXO balances, and something to easier to test.
Moreover, you can add faucet
extrinsic to pallet, that's work too. I let this is challenge for you. Feel free to complete it. The solution is on main branch.
Implement
Firstly, we've to declare genesis data type.
[!Important] Because code is built on
no-std
feature. If we keep using theTransactionOutput
struct, we'll get error... "the trait
Serialize
is not implemented forTransactionOutput
" ...... "the trait
Deserialize<'_>
is not implemented forTransactionOutput
" ...Hence, we need to simplify data type to make it work in both
std
andno-std
feature.
#![allow(unused)] fn main() { ... /// Genesis Utxo Type pub type GenesisUtxoType = (Value, H256); ... #[pallet::genesis_config] pub struct GenesisConfig<T: Config> { pub _ph_data: Option<PhantomData<T>>, pub genesis_utxos: Vec<GenesisUtxoType>, } }
Implement Default
trait for GenesisConfig
:
#![allow(unused)] fn main() { impl<T: Config> Default for GenesisConfig<T> { fn default() -> Self { Self { _ph_data: Default::default(), genesis_utxos: Vec::<GenesisUtxoType>::new(), } } } }
Implement logic to build genesis data
#![allow(unused)] fn main() { #[pallet::genesis_build] impl<T: Config> BuildGenesisConfig for GenesisConfig<T> { fn build(&self) { for utxo in self.genesis_utxos.iter() { let utxo = TransactionOutput { value: utxo.0, pubkey: utxo.1 }; let hash = BlakeTwo256::hash_of(&utxo); Pallet::<T>::store_utxo(&utxo, hash); } } } }
Additionally, I've put some codes to make it easier for testing as mentioned above
#![allow(unused)] fn main() { ... /// Keep track of latest UTXO hash of account /// Mapping from `sr25519::Pubkey` to `BlakeTwo256::hash_of(transaction, index)` /// Just for testing 🫤 /// Because 1 account may have multiple UTXOs #[pallet::storage] #[pallet::getter(fn utxo_of)] pub type UtxoOf<T: Config> = StorageMap<Hasher = Identity, Key = Public, Value = H256, QueryKind = OptionQuery>; ... fn store_utxo(utxo: &TransactionOutput, hash: H256) { ... // Convert H256 back to sr25519::Public let pubkey = Public::from_h256(utxo.pubkey); UtxoOf::<T>::insert(pubkey, hash); } ... }
Move on the node/src/chain_spec.rs
, add some genesis data for pallet UTXO:
...
use academy_pow_runtime::{
AccountId,
SS58Prefix,
Signature,
TOKEN_DECIMALS,
TOKEN_SYMBOL,
WASM_BINARY,
+ utxo::{GenesisUtxoType, Value},
};
...
pub fn development_config() -> Result<ChainSpec, String> {
Ok(ChainSpec::builder(
WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?,
ForkingExtensions {
manual_mode: 1, // change this to `0` if you want to try `auto_fork_validation`
add_sha3_keccak: 10,
remove_md5: 20,
split_sha3_keccak: 30,
maxi_position: String::from("follow-mining"),
},
)
.with_name("Development")
.with_id("dev")
.with_chain_type(ChainType::Development)
.with_genesis_config_patch(genesis(
// Pre-funded accounts
vec![
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
],
// Initial Difficulty
4_000_000,
+ vec![
+ get_account_id_from_seed::<sr25519::Public>("Alice"),
+ get_account_id_from_seed::<sr25519::Public>("Bob"),
+ get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
+ get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
+ ],
))
.with_properties(system_properties())
.build())
}
...
pub fn testnet_config() -> Result<ChainSpec, String> {
Ok(ChainSpec::builder(
WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?,
ForkingExtensions {
manual_mode: 1,
add_sha3_keccak: 0,
remove_md5: 0,
split_sha3_keccak: 0,
maxi_position: String::new(),
},
)
.with_name("Testnet")
.with_id("testnet")
.with_chain_type(ChainType::Local)
.with_genesis_config_patch(genesis(
vec![
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
],
4_000_000,
+ vec![
+ get_account_id_from_seed::<sr25519::Public>("Alice"),
+ get_account_id_from_seed::<sr25519::Public>("Bob"),
+ get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
+ get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
+ ],
))
.with_properties(system_properties())
.build())
}
...
fn genesis(
endowed_accounts: Vec<AccountId>,
initial_difficulty: u32,
+ utxo_genesis_accounts: Vec<AccountId>,
) -> serde_json::Value {
serde_json::json!({
"balances": {
// Configure endowed accounts with initial balance of 1 << 50.
"balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 50)).collect::<Vec<_>>(),
},
"keccakDifficultyAdjustment": {
"initialDifficulty": u32_to_u8_32(initial_difficulty),
},
"md5DifficultyAdjustment": {
"initialDifficulty": u32_to_u8_32(initial_difficulty),
},
"sha3DifficultyAdjustment": {
"initialDifficulty": u32_to_u8_32(initial_difficulty),
},
+ "utxo": {
+ "genesisUtxos": utxo_genesis_accounts
+ .iter().cloned()
+ .map(|k| {
+ let hash = H256::from_slice(&k.as_slice());
+ let value: Value = (1u64 << 50).into();
+ let genesis_utxo: GenesisUtxoType = (value, hash);
+ genesis_utxo
+ }).collect::<Vec<GenesisUtxoType>>(),
+ },
})
}
...
We've add some balances for Alice, Bob, also hard-derive account of Alice and Bob. Let's testing.
Build the code
cargo build --release
# start temporary local node in development environment
./target/release/academy-pow --dev --tmp
[!Note] Because Substrate default account is in SS58 format, so we've to map that address to sr25519 format. Powerful tool to convert
SS58 account
tosr25519::Pubkey
: https://polkadot.subscan.io/tools/format_transform
- Alice: from
5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
to0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
- Bob: from
5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty
to0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48
Direct to https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/chainstate.
Select pallet UTXO > utxoOf
:
- Input:
0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
, press+
button. - Output:
utxo.utxoOf: Option<H256>
0xc670c5f69081da78af400552edcafa3f0f31e84db1b50dd70776e0f87477b3dc`
Select pallet UTXO > utxoStore
:
- Input:
0xc670c5f69081da78af400552edcafa3f0f31e84db1b50dd70776e0f87477b3dc
, press+
button. - Output:
utxo.utxoStore: Option<AcademyPowRuntimeUtxoTransactionOutput>
{
value: 1,125,899,906,842,624
pubkey: 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
}
Great work completing a new step! 💪 You're almost at the finish line. Now, let's dive into last step. Run this command to continue:
#![allow(unused)] fn main() { use std::str::FromStr; use academy_pow_runtime::{ AccountId, SS58Prefix, Signature, TOKEN_DECIMALS, TOKEN_SYMBOL, WASM_BINARY, // TODO [6-genesis-builder] utxo::{GenesisUtxoType, Value}, }; use multi_pow::{ForkHeights, ForkingConfig, MaxiPosition}; use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; use sc_service::ChainType; use serde::{Deserialize, Serialize}; use sp_core::{sr25519, ByteArray, Pair, Public, H256}; use sp_runtime::traits::{IdentifyAccount, Verify}; /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. pub type ChainSpec = sc_service::GenericChainSpec<ForkingExtensions>; /// PoW and Forking related chain spec extensions to configure the client side forking behavior. /// /// The forks here are all related to adding and removing hash algorithms from the PoW. /// The chain begins supporting only md5. Later is adds sha3 and keccak. Later it removes md5. /// And finally there is a contentious fork where people become maxis. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)] #[serde(deny_unknown_fields)] pub struct ForkingExtensions { /// Manual mode is intended for when we you are running a live workshop. /// No forking happens automatically. Rather, you have to hard-code the forks. /// /// If manual mode is enabled, the rest of the parameters are ignored. /// This should really be an enum, but I have to work around the broken extension system. /// /// Aww damn it! I can't even use bool in this broken system? Okay then I guess 0 means /// automatic mode and anything else means manual mode. pub manual_mode: u32, /// The block height to perform the soft fork that adds sha3 and keccak support. pub add_sha3_keccak: u32, /// The block height to perform the hard fork that removes md5 support. pub remove_md5: u32, /// The block height to perform the contentious fork where some become sha3- or keccak-maxis. pub split_sha3_keccak: u32, // Damn extension thing is so fragile, I can't even use an enum here. // Let alone that time I tried to use the forked value feature. /// The political position that this node will take at the contentious fork. pub maxi_position: String, } impl From<&ForkingExtensions> for ForkingConfig { fn from(e: &ForkingExtensions) -> Self { if e.manual_mode > 0 { return Self::Manual; } let fork_heights = ForkHeights { add_sha3_keccak: e.add_sha3_keccak, remove_md5: e.remove_md5, split_sha3_keccak: e.split_sha3_keccak, }; let maxi_position = MaxiPosition::from_str(&e.maxi_position).expect("Should have a valid maxi position..."); Self::Automatic(fork_heights, maxi_position) } } impl ForkingExtensions { /// Try to get the extension from the given `ChainSpec`. pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> { sc_chain_spec::get_extension(chain_spec.extensions()) } } /// Generate a crypto pair from seed. pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public { TPublic::Pair::from_string(&format!("//{}", seed), None) .expect("static values are valid; qed") .public() } type AccountPublic = <Signature as Verify>::Signer; /// Generate an account ID from seed. pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId where AccountPublic: From<<TPublic::Pair as Pair>::Public>, { AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account() } pub fn development_config() -> Result<ChainSpec, String> { Ok(ChainSpec::builder( WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?, ForkingExtensions { manual_mode: 1, // change this to `0` if you want to try `auto_fork_validation` add_sha3_keccak: 10, remove_md5: 20, split_sha3_keccak: 30, maxi_position: String::from("follow-mining"), }, ) .with_name("Development") .with_id("dev") .with_chain_type(ChainType::Development) .with_genesis_config_patch(genesis( // Pre-funded accounts vec![ get_account_id_from_seed::<sr25519::Public>("Alice"), get_account_id_from_seed::<sr25519::Public>("Bob"), get_account_id_from_seed::<sr25519::Public>("Alice//stash"), get_account_id_from_seed::<sr25519::Public>("Bob//stash"), ], // Initial Difficulty 4_000_000, // TODO [6-genesis-builder] // vec![ // get_account_id_from_seed::<sr25519::Public>("Alice"), // get_account_id_from_seed::<sr25519::Public>("Bob"), // get_account_id_from_seed::<sr25519::Public>("Alice//stash"), // get_account_id_from_seed::<sr25519::Public>("Bob//stash"), // ], )) .with_properties(system_properties()) .build()) } pub fn testnet_config() -> Result<ChainSpec, String> { Ok(ChainSpec::builder( WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?, ForkingExtensions { manual_mode: 1, add_sha3_keccak: 0, remove_md5: 0, split_sha3_keccak: 0, maxi_position: String::new(), }, ) .with_name("Testnet") .with_id("testnet") .with_chain_type(ChainType::Local) .with_genesis_config_patch(genesis( vec![ get_account_id_from_seed::<sr25519::Public>("Alice"), get_account_id_from_seed::<sr25519::Public>("Bob"), get_account_id_from_seed::<sr25519::Public>("Alice//stash"), get_account_id_from_seed::<sr25519::Public>("Bob//stash"), ], 4_000_000, // TODO [6-genesis-builder] // vec![ // get_account_id_from_seed::<sr25519::Public>("Alice"), // get_account_id_from_seed::<sr25519::Public>("Bob"), // get_account_id_from_seed::<sr25519::Public>("Alice//stash"), // get_account_id_from_seed::<sr25519::Public>("Bob//stash"), // ], )) .with_properties(system_properties()) .build()) } fn genesis( endowed_accounts: Vec<AccountId>, initial_difficulty: u32, // TODO [6-genesis-builder] // utxo_genesis_accounts: Vec<AccountId>, ) -> serde_json::Value { serde_json::json!({ "balances": { // Configure endowed accounts with initial balance of 1 << 50. "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 50)).collect::<Vec<_>>(), }, "keccakDifficultyAdjustment": { "initialDifficulty": u32_to_u8_32(initial_difficulty), }, "md5DifficultyAdjustment": { "initialDifficulty": u32_to_u8_32(initial_difficulty), }, "sha3DifficultyAdjustment": { "initialDifficulty": u32_to_u8_32(initial_difficulty), }, // TODO [6-genesis-builder] // "utxo": { // "genesisUtxos": utxo_genesis_accounts // .iter().cloned() // .map(|k| { // let hash = H256::from_slice(&k.as_slice()); // let value: Value = (1u64 << 50).into(); // let genesis_utxo: GenesisUtxoType = (value, hash); // genesis_utxo // }).collect::<Vec<GenesisUtxoType>>(), // }, }) } /// Convert u32 (default value) to [u8;32] (U256) /// in little-endian format pub fn u32_to_u8_32(num: u32) -> [u8; 32] { let mut result = [0u8; 32]; let bytes = num.to_le_bytes(); result[..4].copy_from_slice(&bytes); result } fn system_properties() -> sc_chain_spec::Properties { let mut properties = sc_chain_spec::Properties::new(); properties.insert("ss58Format".into(), SS58Prefix::get().into()); properties.insert("tokenSymbol".into(), TOKEN_SYMBOL.into()); properties.insert("tokenDecimals".into(), TOKEN_DECIMALS.into()); properties } }
#![allow(unused)] fn main() { // We make sure this pallet uses `no_std` for compiling to Wasm. #![cfg_attr(not(feature = "std"), no_std)] use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_core::{ sr25519::{Public, Signature}, ByteArray, H256, H512, }; use sp_runtime::traits::{BlakeTwo256, Hash, SaturatedConversion}; use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; use super::{block_author::BlockAuthor, issuance::Issuance}; pub use pallet::*; /// [2-data-structure] pub type Value = u128; /// [2-data-structure] /// Single transaction to be dispatched #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct Transaction { /// UTXOs to be used as inputs for current transaction pub inputs: Vec<TransactionInput>, /// UTXOs to be created as a result of current transaction dispatch pub outputs: Vec<TransactionOutput>, } /// [2-data-structure] /// Single transaction input that refers to one UTXO #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct TransactionInput { /// Reference to an UTXO to be spent pub outpoint: H256, /// Proof that transaction owner is authorized to spend referred UTXO & /// that the entire transaction is untampered pub sigscript: H512, } /// [2-data-structure] /// Single transaction output to create upon transaction dispatch #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct TransactionOutput { /// Value associated with this output pub value: Value, /// Public key associated with this output. In order to spend this output /// owner must provide a proof by hashing the whole `Transaction` and /// signing it with a corresponding private key. pub pubkey: H256, } /// TODO [6-genesis-builder] /// Because code is built on `no-std` feature. /// And we got error: /// ``` /// ... /// the trait `Serialize` is not implemented for `TransactionOutput` /// the trait `Deserialize<'_>` is not implemented for `TransactionOutput` /// ... /// ``` /// /// Hence, we need to simplify data type to make it work in both `std` and `no-std` feature. /// Genesis Utxo Type // pub type GenesisUtxoType = (Value, H256); #[frame_support::pallet(dev_mode)] pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use super::*; /// [2-data-structure] #[pallet::config] pub trait Config: frame_system::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. /// Read more: https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_runtime_types/index.html type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; /// A source to determine the block author type BlockAuthor: BlockAuthor; /// A source to determine the issuance portion of the block reward type Issuance: Issuance<BlockNumberFor<Self>, Value>; } #[pallet::pallet] pub struct Pallet<T>(_); /// [2-data-structure] /// Total reward value to be redistributed among authorities. /// It is accumulated from transactions during block execution /// and then dispersed to validators on block finalization. #[pallet::storage] #[pallet::getter(fn total_reward)] pub type TotalReward<T: Config> = StorageValue<_, Value, ValueQuery>; /// [2-data-structure] /// All valid unspent transaction outputs are stored in this map. /// Initial set of UTXO is populated from the list stored in genesis. /// We use the identity hasher here because the cryptographic hashing is /// done explicitly. /// Mapping from `BlakeTwo256::hash_of(transaction, index)` to `TransactionOutput` #[pallet::storage] #[pallet::getter(fn utxo_store)] pub type UtxoStore<T: Config> = StorageMap< Hasher = Identity, Key = H256, Value = TransactionOutput, QueryKind = OptionQuery, >; /// TODO [6-genesis-builder] /// Keep track of latest UTXO hash of account /// Mapping from `sr25519::Pubkey` to `BlakeTwo256::hash_of(transaction, index)` /// Just for testing 🫤 /// Because 1 account may have multiple UTXOs // #[pallet::storage] // #[pallet::getter(fn utxo_of)] // pub type UtxoOf<T: Config> = // StorageMap<Hasher = Identity, Key = Public, Value = H256, QueryKind = OptionQuery>; /// TODO [6-genesis-builder] // #[pallet::genesis_config] // pub struct GenesisConfig<T: Config> { // pub _ph_data: Option<PhantomData<T>>, // pub genesis_utxos: Vec<GenesisUtxoType>, // } /// TODO [6-genesis-builder] // impl<T: Config> Default for GenesisConfig<T> { // fn default() -> Self { // Self { // _ph_data: Default::default(), // genesis_utxos: Vec::<GenesisUtxoType>::new(), // } // } // } /// TODO [6-genesis-builder] // #[pallet::genesis_build] // impl<T: Config> BuildGenesisConfig for GenesisConfig<T> { // fn build(&self) { // for utxo in self.genesis_utxos.iter() { // let utxo = TransactionOutput { value: utxo.0, pubkey: utxo.1 }; // let hash = BlakeTwo256::hash_of(&utxo); // Pallet::<T>::store_utxo(&utxo, hash); // } // } // } /// [2-data-structure] /// Pallets use events to inform users when important changes are made. #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config> { /// Dispatch transaction successful TransactionSuccess(Transaction), /// UTXO out processed TransactionOutputProcessed(H256), /// Reward distributed to `BlockAuthor` RewardDistributed(Value, H256), /// Faucet to `To` Faucet(Value, H256), /// No one get reward RewardWasted, } /// [2-data-structure] /// Errors inform users that something went wrong. #[pallet::error] pub enum Error<T> { /// Missing `Transaction` Input MissingInput, /// Reward overflow RewardOverflow, /// Maximum transaction depth MaximumTransactionDepth, /// Empty input EmptyInput, /// Empty output EmptyOutput, /// Each input must only be used once DuplicatedInput, /// Each output must be defined only once DuplicatedOutput, /// Input value is overflow InputOverflow, /// Output value is overflow OutputOverflow, /// Output spent must lte than Input spent OutputOverInput, /// Zero amount spent ZeroAmount, /// Invalid signature InvalidSignature, } /// [4-dispersed-reward] #[pallet::hooks] impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { fn on_finalize(_n: BlockNumberFor<T>) { match T::BlockAuthor::block_author() { // Block author did not provide key to claim reward None => Self::deposit_event(Event::RewardWasted), // Block author did provide key, so issue thir reward Some(author) => Self::disperse_reward(&author), } } } /// [2-data-structure] /// Define extrinsics / dispatchable function #[pallet::call] impl<T: Config> Pallet<T> { pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult { // [3-spend-utxo] // validate transaction let transaction_validity = Self::validate_transaction(&transaction)?; ensure!( transaction_validity.requires.is_empty(), Error::<T>::MissingInput ); // implement logic Self::do_spend(&transaction, transaction_validity.priority as Value)?; // emit event Self::deposit_event(Event::<T>::TransactionSuccess(transaction)); Ok(()) } } /// [2-data-structure] /// Define intrinsics impl<T: Config> Pallet<T> { /// Implement spend logic, update storage to reflect changes made by transaction /// Where each UTXO key is a hash of the entire transaction and its order in the `TransactionOutputs` vector fn do_spend(transaction: &Transaction, reward: Value) -> DispatchResult { // [3-spend-utxo] // Calculate new reward total. The rest of `total_input - total_output` will be used for block reward. let new_total = TotalReward::<T>::get() .checked_add(reward) .ok_or(Error::<T>::RewardOverflow)?; TotalReward::<T>::put(new_total); // Removing spent UTXOs for input in &transaction.inputs { UtxoStore::<T>::remove(input.outpoint); } let mut index: u64 = 0; for output in &transaction.outputs { let hash = BlakeTwo256::hash_of(&(&transaction.encode(), index)); // validated before, this is safe index = index .checked_add(1) .ok_or(Error::<T>::MaximumTransactionDepth) .unwrap(); Self::store_utxo(output, hash); Self::deposit_event(Event::TransactionOutputProcessed(hash)); } Ok(()) } /// Redistribute combined reward value to block Author fn disperse_reward(author: &Public) { // [4-dispersed-reward] // take the rest of reward // plus issuance reward of current block number let reward = TotalReward::<T>::take() + T::Issuance::issuance(frame_system::Pallet::<T>::block_number()); let utxo = TransactionOutput { value: reward, pubkey: H256::from_slice(author.as_slice()), }; let hash = BlakeTwo256::hash_of(&( &utxo, frame_system::Pallet::<T>::block_number().saturated_into::<u64>(), )); Self::store_utxo(&utxo, hash); Self::deposit_event(Event::RewardDistributed(reward, hash)); } /// Mutate storage, insert / update new UTXOs fn store_utxo(utxo: &TransactionOutput, hash: H256) { // [3-spend-utxo] UtxoStore::<T>::insert(hash, utxo); // TODO [6-genesis-builder] // Convert H256 back to sr25519::Public // let pubkey = Public::from_h256(utxo.pubkey); // UtxoOf::<T>::insert(pubkey, hash); } /// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash. fn get_simple_transaction(transaction: &Transaction) -> Vec<u8> { // [3-spend-utxo] let mut trx = transaction.clone(); for input in trx.inputs.iter_mut() { input.sigscript = H512::zero(); } trx.encode() } /// Check transaction for validity, errors, & race conditions /// Called by both transaction pool and runtime execution pub fn validate_transaction( transaction: &Transaction, ) -> Result<ValidTransaction, &'static str> { // [3-spend-utxo] // Check inputs and outputs are not empty ensure!(!transaction.inputs.is_empty(), Error::<T>::EmptyInput); ensure!(!transaction.outputs.is_empty(), Error::<T>::EmptyOutput); // Check each input is used exactly once { let input_set: BTreeMap<_, ()> = transaction.inputs.iter().map(|input| (input, ())).collect(); ensure!( input_set.len() == transaction.inputs.len(), Error::<T>::DuplicatedInput ); } { let output_set: BTreeMap<_, ()> = transaction .outputs .iter() .map(|output| (output, ())) .collect(); ensure!( output_set.len() == transaction.outputs.len(), Error::<T>::DuplicatedOutput ); } let mut total_input: Value = 0; let mut total_output: Value = 0; let mut output_index: u64 = 0; let simple_transaction = Self::get_simple_transaction(transaction); // Variables sent to transaction pool let mut missing_utxos = Vec::new(); let mut new_utxos = Vec::new(); let mut reward = 0; for input in transaction.inputs.iter() { // Check all inputs match to existing, unspent and unlocked outputs if let Some(input_utxo) = UtxoStore::<T>::get(&input.outpoint) { // Check provided signatures are valid let is_valid_sig = sp_io::crypto::sr25519_verify( &Signature::from_raw(*input.sigscript.as_fixed_bytes()), &simple_transaction, &Public::from_h256(input_utxo.pubkey), ); ensure!(is_valid_sig, Error::<T>::InvalidSignature); // Check sum of input values does not overflow total_input = total_input .checked_add(input_utxo.value) .ok_or(Error::<T>::InputOverflow)?; } else { missing_utxos.push(input.outpoint.clone().as_fixed_bytes().to_vec()); } } // Check each output is defined exactly once and has nonzero value for output in transaction.outputs.iter() { ensure!(output.value > 0, Error::<T>::ZeroAmount); let hash = BlakeTwo256::hash_of(&(&transaction.encode(), output_index)); output_index = output_index .checked_add(1) .ok_or(Error::<T>::MaximumTransactionDepth)?; // Check new outputs do not collide with existing ones ensure!( !UtxoStore::<T>::contains_key(hash), Error::<T>::DuplicatedOutput ); // Check sum of output values does not overflow total_output = total_output .checked_add(output.value) .ok_or(Error::<T>::OutputOverflow)?; new_utxos.push(hash.as_fixed_bytes().to_vec()); } // If no race condition, check the math if missing_utxos.is_empty() { // Check total output value must not exceed total input value ensure!(total_input >= total_output, Error::<T>::OutputOverInput); reward = total_input .checked_sub(total_output) .ok_or(Error::<T>::RewardOverflow)?; } // Returns transaction details Ok(ValidTransaction { requires: missing_utxos, provides: new_utxos, priority: reward as u64, longevity: TransactionLongevity::max_value(), propagate: true, }) } } } }
#![allow(unused)] fn main() { use std::str::FromStr; use academy_pow_runtime::{ AccountId, SS58Prefix, Signature, TOKEN_DECIMALS, TOKEN_SYMBOL, WASM_BINARY, // [6-genesis-builder] utxo::{GenesisUtxoType, Value}, }; use multi_pow::{ForkHeights, ForkingConfig, MaxiPosition}; use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup}; use sc_service::ChainType; use serde::{Deserialize, Serialize}; use sp_core::{sr25519, ByteArray, Pair, Public, H256}; use sp_runtime::traits::{IdentifyAccount, Verify}; /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. pub type ChainSpec = sc_service::GenericChainSpec<ForkingExtensions>; /// PoW and Forking related chain spec extensions to configure the client side forking behavior. /// /// The forks here are all related to adding and removing hash algorithms from the PoW. /// The chain begins supporting only md5. Later is adds sha3 and keccak. Later it removes md5. /// And finally there is a contentious fork where people become maxis. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ChainSpecGroup, ChainSpecExtension)] #[serde(deny_unknown_fields)] pub struct ForkingExtensions { /// Manual mode is intended for when we you are running a live workshop. /// No forking happens automatically. Rather, you have to hard-code the forks. /// /// If manual mode is enabled, the rest of the parameters are ignored. /// This should really be an enum, but I have to work around the broken extension system. /// /// Aww damn it! I can't even use bool in this broken system? Okay then I guess 0 means /// automatic mode and anything else means manual mode. pub manual_mode: u32, /// The block height to perform the soft fork that adds sha3 and keccak support. pub add_sha3_keccak: u32, /// The block height to perform the hard fork that removes md5 support. pub remove_md5: u32, /// The block height to perform the contentious fork where some become sha3- or keccak-maxis. pub split_sha3_keccak: u32, // Damn extension thing is so fragile, I can't even use an enum here. // Let alone that time I tried to use the forked value feature. /// The political position that this node will take at the contentious fork. pub maxi_position: String, } impl From<&ForkingExtensions> for ForkingConfig { fn from(e: &ForkingExtensions) -> Self { if e.manual_mode > 0 { return Self::Manual; } let fork_heights = ForkHeights { add_sha3_keccak: e.add_sha3_keccak, remove_md5: e.remove_md5, split_sha3_keccak: e.split_sha3_keccak, }; let maxi_position = MaxiPosition::from_str(&e.maxi_position).expect("Should have a valid maxi position..."); Self::Automatic(fork_heights, maxi_position) } } impl ForkingExtensions { /// Try to get the extension from the given `ChainSpec`. pub fn try_get(chain_spec: &dyn sc_service::ChainSpec) -> Option<&Self> { sc_chain_spec::get_extension(chain_spec.extensions()) } } /// Generate a crypto pair from seed. pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public { TPublic::Pair::from_string(&format!("//{}", seed), None) .expect("static values are valid; qed") .public() } type AccountPublic = <Signature as Verify>::Signer; /// Generate an account ID from seed. pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId where AccountPublic: From<<TPublic::Pair as Pair>::Public>, { AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account() } pub fn development_config() -> Result<ChainSpec, String> { Ok(ChainSpec::builder( WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?, ForkingExtensions { manual_mode: 1, // change this to `0` if you want to try `auto_fork_validation` add_sha3_keccak: 10, remove_md5: 20, split_sha3_keccak: 30, maxi_position: String::from("follow-mining"), }, ) .with_name("Development") .with_id("dev") .with_chain_type(ChainType::Development) .with_genesis_config_patch(genesis( // Pre-funded accounts vec![ get_account_id_from_seed::<sr25519::Public>("Alice"), get_account_id_from_seed::<sr25519::Public>("Bob"), get_account_id_from_seed::<sr25519::Public>("Alice//stash"), get_account_id_from_seed::<sr25519::Public>("Bob//stash"), ], // Initial Difficulty 4_000_000, // [6-genesis-builder] vec![ get_account_id_from_seed::<sr25519::Public>("Alice"), get_account_id_from_seed::<sr25519::Public>("Bob"), get_account_id_from_seed::<sr25519::Public>("Alice//stash"), get_account_id_from_seed::<sr25519::Public>("Bob//stash"), ], )) .with_properties(system_properties()) .build()) } pub fn testnet_config() -> Result<ChainSpec, String> { Ok(ChainSpec::builder( WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?, ForkingExtensions { manual_mode: 1, add_sha3_keccak: 0, remove_md5: 0, split_sha3_keccak: 0, maxi_position: String::new(), }, ) .with_name("Testnet") .with_id("testnet") .with_chain_type(ChainType::Local) .with_genesis_config_patch(genesis( vec![ get_account_id_from_seed::<sr25519::Public>("Alice"), get_account_id_from_seed::<sr25519::Public>("Bob"), get_account_id_from_seed::<sr25519::Public>("Alice//stash"), get_account_id_from_seed::<sr25519::Public>("Bob//stash"), ], 4_000_000, // [6-genesis-builder] vec![ get_account_id_from_seed::<sr25519::Public>("Alice"), get_account_id_from_seed::<sr25519::Public>("Bob"), get_account_id_from_seed::<sr25519::Public>("Alice//stash"), get_account_id_from_seed::<sr25519::Public>("Bob//stash"), ], )) .with_properties(system_properties()) .build()) } fn genesis( endowed_accounts: Vec<AccountId>, initial_difficulty: u32, // [6-genesis-builder] utxo_genesis_accounts: Vec<AccountId>, ) -> serde_json::Value { serde_json::json!({ "balances": { // Configure endowed accounts with initial balance of 1 << 50. "balances": endowed_accounts.iter().cloned().map(|k| (k, 1u64 << 50)).collect::<Vec<_>>(), }, "keccakDifficultyAdjustment": { "initialDifficulty": u32_to_u8_32(initial_difficulty), }, "md5DifficultyAdjustment": { "initialDifficulty": u32_to_u8_32(initial_difficulty), }, "sha3DifficultyAdjustment": { "initialDifficulty": u32_to_u8_32(initial_difficulty), }, // [6-genesis-builder] "utxo": { "genesisUtxos": utxo_genesis_accounts .iter().cloned() .map(|k| { let hash = H256::from_slice(&k.as_slice()); let value: Value = (1u64 << 50).into(); let genesis_utxo: GenesisUtxoType = (value, hash); genesis_utxo }).collect::<Vec<GenesisUtxoType>>(), }, }) } /// Convert u32 (default value) to [u8;32] (U256) /// in little-endian format pub fn u32_to_u8_32(num: u32) -> [u8; 32] { let mut result = [0u8; 32]; let bytes = num.to_le_bytes(); result[..4].copy_from_slice(&bytes); result } fn system_properties() -> sc_chain_spec::Properties { let mut properties = sc_chain_spec::Properties::new(); properties.insert("ss58Format".into(), SS58Prefix::get().into()); properties.insert("tokenSymbol".into(), TOKEN_SYMBOL.into()); properties.insert("tokenDecimals".into(), TOKEN_DECIMALS.into()); properties } }
#![allow(unused)] fn main() { // We make sure this pallet uses `no_std` for compiling to Wasm. #![cfg_attr(not(feature = "std"), no_std)] use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; use sp_core::{ sr25519::{Public, Signature}, ByteArray, H256, H512, }; use sp_runtime::traits::{BlakeTwo256, Hash, SaturatedConversion}; use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; use super::{block_author::BlockAuthor, issuance::Issuance}; pub use pallet::*; /// [2-data-structure] pub type Value = u128; /// [2-data-structure] /// Single transaction to be dispatched #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct Transaction { /// UTXOs to be used as inputs for current transaction pub inputs: Vec<TransactionInput>, /// UTXOs to be created as a result of current transaction dispatch pub outputs: Vec<TransactionOutput>, } /// [2-data-structure] /// Single transaction input that refers to one UTXO #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct TransactionInput { /// Reference to an UTXO to be spent pub outpoint: H256, /// Proof that transaction owner is authorized to spend referred UTXO & /// that the entire transaction is untampered pub sigscript: H512, } /// [2-data-structure] /// Single transaction output to create upon transaction dispatch #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Default, Clone, Encode, Decode, Hash, Debug, TypeInfo)] pub struct TransactionOutput { /// Value associated with this output pub value: Value, /// Public key associated with this output. In order to spend this output /// owner must provide a proof by hashing the whole `Transaction` and /// signing it with a corresponding private key. pub pubkey: H256, } /// [6-genesis-builder] /// Because code is built on `no-std` feature. /// And we got error: /// ``` /// ... /// the trait `Serialize` is not implemented for `TransactionOutput` /// the trait `Deserialize<'_>` is not implemented for `TransactionOutput` /// ... /// ``` /// /// Hence, we need to simplify data type to make it work in both `std` and `no-std` feature. /// Genesis Utxo Type pub type GenesisUtxoType = (Value, H256); #[frame_support::pallet(dev_mode)] pub mod pallet { use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use super::*; /// [2-data-structure] #[pallet::config] pub trait Config: frame_system::Config { /// Because this pallet emits events, it depends on the runtime's definition of an event. /// Read more: https://paritytech.github.io/polkadot-sdk/master/polkadot_sdk_docs/reference_docs/frame_runtime_types/index.html type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; /// A source to determine the block author type BlockAuthor: BlockAuthor; /// A source to determine the issuance portion of the block reward type Issuance: Issuance<BlockNumberFor<Self>, Value>; } #[pallet::pallet] pub struct Pallet<T>(_); /// [2-data-structure] /// Total reward value to be redistributed among authorities. /// It is accumulated from transactions during block execution /// and then dispersed to validators on block finalization. #[pallet::storage] #[pallet::getter(fn total_reward)] pub type TotalReward<T: Config> = StorageValue<_, Value, ValueQuery>; /// [2-data-structure] /// All valid unspent transaction outputs are stored in this map. /// Initial set of UTXO is populated from the list stored in genesis. /// We use the identity hasher here because the cryptographic hashing is /// done explicitly. /// Mapping from `BlakeTwo256::hash_of(transaction, index)` to `TransactionOutput` #[pallet::storage] #[pallet::getter(fn utxo_store)] pub type UtxoStore<T: Config> = StorageMap< Hasher = Identity, Key = H256, Value = TransactionOutput, QueryKind = OptionQuery, >; /// [6-genesis-builder] /// Keep track of latest UTXO hash of account /// Mapping from `sr25519::Pubkey` to `BlakeTwo256::hash_of(transaction, index)` /// Just for testing 🫤 /// Because 1 account may have multiple UTXOs #[pallet::storage] #[pallet::getter(fn utxo_of)] pub type UtxoOf<T: Config> = StorageMap<Hasher = Identity, Key = Public, Value = H256, QueryKind = OptionQuery>; /// [6-genesis-builder] #[pallet::genesis_config] pub struct GenesisConfig<T: Config> { pub _ph_data: Option<PhantomData<T>>, pub genesis_utxos: Vec<GenesisUtxoType>, } /// [6-genesis-builder] impl<T: Config> Default for GenesisConfig<T> { fn default() -> Self { Self { _ph_data: Default::default(), genesis_utxos: Vec::<GenesisUtxoType>::new(), } } } /// [6-genesis-builder] #[pallet::genesis_build] impl<T: Config> BuildGenesisConfig for GenesisConfig<T> { fn build(&self) { for utxo in self.genesis_utxos.iter() { let utxo = TransactionOutput { value: utxo.0, pubkey: utxo.1 }; let hash = BlakeTwo256::hash_of(&utxo); Pallet::<T>::store_utxo(&utxo, hash); } } } /// [2-data-structure] /// Pallets use events to inform users when important changes are made. #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event<T: Config> { /// Dispatch transaction successful TransactionSuccess(Transaction), /// UTXO out processed TransactionOutputProcessed(H256), /// Reward distributed to `BlockAuthor` RewardDistributed(Value, H256), /// Faucet to `To` Faucet(Value, H256), /// No one get reward RewardWasted, } /// [2-data-structure] /// Errors inform users that something went wrong. #[pallet::error] pub enum Error<T> { /// Missing `Transaction` Input MissingInput, /// Reward overflow RewardOverflow, /// Maximum transaction depth MaximumTransactionDepth, /// Empty input EmptyInput, /// Empty output EmptyOutput, /// Each input must only be used once DuplicatedInput, /// Each output must be defined only once DuplicatedOutput, /// Input value is overflow InputOverflow, /// Output value is overflow OutputOverflow, /// Output spent must lte than Input spent OutputOverInput, /// Zero amount spent ZeroAmount, /// Invalid signature InvalidSignature, } /// [4-dispersed-reward] #[pallet::hooks] impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { fn on_finalize(_n: BlockNumberFor<T>) { match T::BlockAuthor::block_author() { // Block author did not provide key to claim reward None => Self::deposit_event(Event::RewardWasted), // Block author did provide key, so issue thir reward Some(author) => Self::disperse_reward(&author), } } } /// [2-data-structure] /// Define extrinsics / dispatchable function #[pallet::call] impl<T: Config> Pallet<T> { pub fn spend(_origin: OriginFor<T>, transaction: Transaction) -> DispatchResult { // [3-spend-utxo] // validate transaction let transaction_validity = Self::validate_transaction(&transaction)?; ensure!( transaction_validity.requires.is_empty(), Error::<T>::MissingInput ); // implement logic Self::do_spend(&transaction, transaction_validity.priority as Value)?; // emit event Self::deposit_event(Event::<T>::TransactionSuccess(transaction)); Ok(()) } } /// [2-data-structure] /// Define intrinsics impl<T: Config> Pallet<T> { /// Implement spend logic, update storage to reflect changes made by transaction /// Where each UTXO key is a hash of the entire transaction and its order in the `TransactionOutputs` vector fn do_spend(transaction: &Transaction, reward: Value) -> DispatchResult { // [3-spend-utxo] // Calculate new reward total. The rest of `total_input - total_output` will be used for block reward. let new_total = TotalReward::<T>::get() .checked_add(reward) .ok_or(Error::<T>::RewardOverflow)?; TotalReward::<T>::put(new_total); // Removing spent UTXOs for input in &transaction.inputs { UtxoStore::<T>::remove(input.outpoint); } let mut index: u64 = 0; for output in &transaction.outputs { let hash = BlakeTwo256::hash_of(&(&transaction.encode(), index)); // validated before, this is safe index = index .checked_add(1) .ok_or(Error::<T>::MaximumTransactionDepth) .unwrap(); Self::store_utxo(output, hash); Self::deposit_event(Event::TransactionOutputProcessed(hash)); } Ok(()) } /// Redistribute combined reward value to block Author fn disperse_reward(author: &Public) { // [4-dispersed-reward] // take the rest of reward // plus issuance reward of current block number let reward = TotalReward::<T>::take() + T::Issuance::issuance(frame_system::Pallet::<T>::block_number()); let utxo = TransactionOutput { value: reward, pubkey: H256::from_slice(author.as_slice()), }; let hash = BlakeTwo256::hash_of(&( &utxo, frame_system::Pallet::<T>::block_number().saturated_into::<u64>(), )); Self::store_utxo(&utxo, hash); Self::deposit_event(Event::RewardDistributed(reward, hash)); } /// Mutate storage, insert / update new UTXOs fn store_utxo(utxo: &TransactionOutput, hash: H256) { // [3-spend-utxo] UtxoStore::<T>::insert(hash, utxo); // [6-genesis-builder] // Convert H256 back to sr25519::Public let pubkey = Public::from_h256(utxo.pubkey); UtxoOf::<T>::insert(pubkey, hash); } /// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash. fn get_simple_transaction(transaction: &Transaction) -> Vec<u8> { // [3-spend-utxo] let mut trx = transaction.clone(); for input in trx.inputs.iter_mut() { input.sigscript = H512::zero(); } trx.encode() } /// Check transaction for validity, errors, & race conditions /// Called by both transaction pool and runtime execution pub fn validate_transaction( transaction: &Transaction, ) -> Result<ValidTransaction, &'static str> { // [3-spend-utxo] // Check inputs and outputs are not empty ensure!(!transaction.inputs.is_empty(), Error::<T>::EmptyInput); ensure!(!transaction.outputs.is_empty(), Error::<T>::EmptyOutput); // Check each input is used exactly once { let input_set: BTreeMap<_, ()> = transaction.inputs.iter().map(|input| (input, ())).collect(); ensure!( input_set.len() == transaction.inputs.len(), Error::<T>::DuplicatedInput ); } { let output_set: BTreeMap<_, ()> = transaction .outputs .iter() .map(|output| (output, ())) .collect(); ensure!( output_set.len() == transaction.outputs.len(), Error::<T>::DuplicatedOutput ); } let mut total_input: Value = 0; let mut total_output: Value = 0; let mut output_index: u64 = 0; let simple_transaction = Self::get_simple_transaction(transaction); // Variables sent to transaction pool let mut missing_utxos = Vec::new(); let mut new_utxos = Vec::new(); let mut reward = 0; for input in transaction.inputs.iter() { // Check all inputs match to existing, unspent and unlocked outputs if let Some(input_utxo) = UtxoStore::<T>::get(&input.outpoint) { // Check provided signatures are valid let is_valid_sig = sp_io::crypto::sr25519_verify( &Signature::from_raw(*input.sigscript.as_fixed_bytes()), &simple_transaction, &Public::from_h256(input_utxo.pubkey), ); ensure!(is_valid_sig, Error::<T>::InvalidSignature); // Check sum of input values does not overflow total_input = total_input .checked_add(input_utxo.value) .ok_or(Error::<T>::InputOverflow)?; } else { missing_utxos.push(input.outpoint.clone().as_fixed_bytes().to_vec()); } } // Check each output is defined exactly once and has nonzero value for output in transaction.outputs.iter() { ensure!(output.value > 0, Error::<T>::ZeroAmount); let hash = BlakeTwo256::hash_of(&(&transaction.encode(), output_index)); output_index = output_index .checked_add(1) .ok_or(Error::<T>::MaximumTransactionDepth)?; // Check new outputs do not collide with existing ones ensure!( !UtxoStore::<T>::contains_key(hash), Error::<T>::DuplicatedOutput ); // Check sum of output values does not overflow total_output = total_output .checked_add(output.value) .ok_or(Error::<T>::OutputOverflow)?; new_utxos.push(hash.as_fixed_bytes().to_vec()); } // If no race condition, check the math if missing_utxos.is_empty() { // Check total output value must not exceed total input value ensure!(total_input >= total_output, Error::<T>::OutputOverInput); reward = total_input .checked_sub(total_output) .ok_or(Error::<T>::RewardOverflow)?; } // Returns transaction details Ok(ValidTransaction { requires: missing_utxos, provides: new_utxos, priority: reward as u64, longevity: TransactionLongevity::max_value(), propagate: true, }) } } } }
diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs
index 091d0e8..90e5e84 100644
--- a/node/src/chain_spec.rs
+++ b/node/src/chain_spec.rs
@@ -2,6 +2,8 @@ use std::str::FromStr;
use academy_pow_runtime::{
AccountId, SS58Prefix, Signature, TOKEN_DECIMALS, TOKEN_SYMBOL, WASM_BINARY,
+ // TODO [6-genesis-builder]
+ utxo::{GenesisUtxoType, Value},
};
use multi_pow::{ForkHeights, ForkingConfig, MaxiPosition};
use sc_chain_spec::{ChainSpecExtension, ChainSpecGroup};
@@ -109,6 +111,13 @@ pub fn development_config() -> Result<ChainSpec, String> {
],
// Initial Difficulty
4_000_000,
+ // TODO [6-genesis-builder]
+ // vec![
+ // get_account_id_from_seed::<sr25519::Public>("Alice"),
+ // get_account_id_from_seed::<sr25519::Public>("Bob"),
+ // get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
+ // get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
+ // ],
))
.with_properties(system_properties())
.build())
@@ -136,12 +145,24 @@ pub fn testnet_config() -> Result<ChainSpec, String> {
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
],
4_000_000,
+ // TODO [6-genesis-builder]
+ // vec![
+ // get_account_id_from_seed::<sr25519::Public>("Alice"),
+ // get_account_id_from_seed::<sr25519::Public>("Bob"),
+ // get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
+ // get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
+ // ],
))
.with_properties(system_properties())
.build())
}
-fn genesis(endowed_accounts: Vec<AccountId>, initial_difficulty: u32) -> serde_json::Value {
+fn genesis(
+ endowed_accounts: Vec<AccountId>,
+ initial_difficulty: u32,
+ // TODO [6-genesis-builder]
+ // utxo_genesis_accounts: Vec<AccountId>,
+) -> serde_json::Value {
serde_json::json!({
"balances": {
// Configure endowed accounts with initial balance of 1 << 50.
@@ -156,6 +177,18 @@ fn genesis(endowed_accounts: Vec<AccountId>, initial_difficulty: u32) -> serde_j
"sha3DifficultyAdjustment": {
"initialDifficulty": u32_to_u8_32(initial_difficulty),
},
+ // TODO [6-genesis-builder]
+ // "utxo": {
+ // "genesisUtxos": utxo_genesis_accounts
+ // .iter().cloned()
+ // .map(|k| {
+ // let hash = H256::from_slice(&k.as_slice());
+ // let value: Value = (1u64 << 50).into();
+ // let genesis_utxo: GenesisUtxoType = (value, hash);
+
+ // genesis_utxo
+ // }).collect::<Vec<GenesisUtxoType>>(),
+ // },
})
}
diff --git a/runtime/src/utxo.rs b/runtime/src/utxo.rs
index ed8ec6a..0632046 100644
--- a/runtime/src/utxo.rs
+++ b/runtime/src/utxo.rs
@@ -57,6 +57,21 @@ pub struct TransactionOutput {
pub pubkey: H256,
}
+
+/// TODO [6-genesis-builder]
+/// Because code is built on `no-std` feature.
+/// And we got error:
+/// ```
+/// ...
+/// the trait `Serialize` is not implemented for `TransactionOutput`
+/// the trait `Deserialize<'_>` is not implemented for `TransactionOutput`
+/// ...
+/// ```
+///
+/// Hence, we need to simplify data type to make it work in both `std` and `no-std` feature.
+/// Genesis Utxo Type
+// pub type GenesisUtxoType = (Value, H256);
+
#[frame_support::pallet(dev_mode)]
pub mod pallet {
use frame_support::pallet_prelude::*;
@@ -105,6 +120,44 @@ pub mod pallet {
QueryKind = OptionQuery,
>;
+ /// TODO [6-genesis-builder]
+ /// Keep track of latest UTXO hash of account
+ /// Mapping from `sr25519::Pubkey` to `BlakeTwo256::hash_of(transaction, index)`
+ /// Just for testing 🫤
+ /// Because 1 account may have multiple UTXOs
+ // #[pallet::storage]
+ // #[pallet::getter(fn utxo_of)]
+ // pub type UtxoOf<T: Config> =
+ // StorageMap<Hasher = Identity, Key = Public, Value = H256, QueryKind = OptionQuery>;
+
+ /// TODO [6-genesis-builder]
+ // #[pallet::genesis_config]
+ // pub struct GenesisConfig<T: Config> {
+ // pub _ph_data: Option<PhantomData<T>>,
+ // pub genesis_utxos: Vec<GenesisUtxoType>,
+ // }
+
+ /// TODO [6-genesis-builder]
+ // impl<T: Config> Default for GenesisConfig<T> {
+ // fn default() -> Self {
+ // Self {
+ // _ph_data: Default::default(),
+ // genesis_utxos: Vec::<GenesisUtxoType>::new(),
+ // }
+ // }
+ // }
+
+ /// TODO [6-genesis-builder]
+ // #[pallet::genesis_build]
+ // impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
+ // fn build(&self) {
+ // for utxo in self.genesis_utxos.iter() {
+ // let utxo = TransactionOutput { value: utxo.0, pubkey: utxo.1 };
+ // let hash = BlakeTwo256::hash_of(&utxo);
+ // Pallet::<T>::store_utxo(&utxo, hash);
+ // }
+ // }
+ // }
/// [2-data-structure]
/// Pallets use events to inform users when important changes are made.
@@ -250,7 +303,10 @@ pub mod pallet {
// [3-spend-utxo]
UtxoStore::<T>::insert(hash, utxo);
- // // further update 😉
+ // TODO [6-genesis-builder]
+ // Convert H256 back to sr25519::Public
+ // let pubkey = Public::from_h256(utxo.pubkey);
+ // UtxoOf::<T>::insert(pubkey, hash);
}
/// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash.
diff --git a/node/src/chain_spec.rs b/node/src/chain_spec.rs
index 90e5e84..71fc6c7 100644
--- a/node/src/chain_spec.rs
+++ b/node/src/chain_spec.rs
@@ -2,7 +2,7 @@ use std::str::FromStr;
use academy_pow_runtime::{
AccountId, SS58Prefix, Signature, TOKEN_DECIMALS, TOKEN_SYMBOL, WASM_BINARY,
- // TODO [6-genesis-builder]
+ // [6-genesis-builder]
utxo::{GenesisUtxoType, Value},
};
use multi_pow::{ForkHeights, ForkingConfig, MaxiPosition};
@@ -111,13 +111,13 @@ pub fn development_config() -> Result<ChainSpec, String> {
],
// Initial Difficulty
4_000_000,
- // TODO [6-genesis-builder]
- // vec![
- // get_account_id_from_seed::<sr25519::Public>("Alice"),
- // get_account_id_from_seed::<sr25519::Public>("Bob"),
- // get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
- // get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
- // ],
+ // [6-genesis-builder]
+ vec![
+ get_account_id_from_seed::<sr25519::Public>("Alice"),
+ get_account_id_from_seed::<sr25519::Public>("Bob"),
+ get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
+ get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
+ ],
))
.with_properties(system_properties())
.build())
@@ -145,13 +145,13 @@ pub fn testnet_config() -> Result<ChainSpec, String> {
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
],
4_000_000,
- // TODO [6-genesis-builder]
- // vec![
- // get_account_id_from_seed::<sr25519::Public>("Alice"),
- // get_account_id_from_seed::<sr25519::Public>("Bob"),
- // get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
- // get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
- // ],
+ // [6-genesis-builder]
+ vec![
+ get_account_id_from_seed::<sr25519::Public>("Alice"),
+ get_account_id_from_seed::<sr25519::Public>("Bob"),
+ get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
+ get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
+ ],
))
.with_properties(system_properties())
.build())
@@ -160,8 +160,8 @@ pub fn testnet_config() -> Result<ChainSpec, String> {
fn genesis(
endowed_accounts: Vec<AccountId>,
initial_difficulty: u32,
- // TODO [6-genesis-builder]
- // utxo_genesis_accounts: Vec<AccountId>,
+ // [6-genesis-builder]
+ utxo_genesis_accounts: Vec<AccountId>,
) -> serde_json::Value {
serde_json::json!({
"balances": {
@@ -177,18 +177,18 @@ fn genesis(
"sha3DifficultyAdjustment": {
"initialDifficulty": u32_to_u8_32(initial_difficulty),
},
- // TODO [6-genesis-builder]
- // "utxo": {
- // "genesisUtxos": utxo_genesis_accounts
- // .iter().cloned()
- // .map(|k| {
- // let hash = H256::from_slice(&k.as_slice());
- // let value: Value = (1u64 << 50).into();
- // let genesis_utxo: GenesisUtxoType = (value, hash);
-
- // genesis_utxo
- // }).collect::<Vec<GenesisUtxoType>>(),
- // },
+ // [6-genesis-builder]
+ "utxo": {
+ "genesisUtxos": utxo_genesis_accounts
+ .iter().cloned()
+ .map(|k| {
+ let hash = H256::from_slice(&k.as_slice());
+ let value: Value = (1u64 << 50).into();
+ let genesis_utxo: GenesisUtxoType = (value, hash);
+
+ genesis_utxo
+ }).collect::<Vec<GenesisUtxoType>>(),
+ },
})
}
diff --git a/runtime/src/utxo.rs b/runtime/src/utxo.rs
index 0632046..5f0bda0 100644
--- a/runtime/src/utxo.rs
+++ b/runtime/src/utxo.rs
@@ -58,7 +58,7 @@ pub struct TransactionOutput {
}
-/// TODO [6-genesis-builder]
+/// [6-genesis-builder]
/// Because code is built on `no-std` feature.
/// And we got error:
/// ```
@@ -70,7 +70,7 @@ pub struct TransactionOutput {
///
/// Hence, we need to simplify data type to make it work in both `std` and `no-std` feature.
/// Genesis Utxo Type
-// pub type GenesisUtxoType = (Value, H256);
+pub type GenesisUtxoType = (Value, H256);
#[frame_support::pallet(dev_mode)]
pub mod pallet {
@@ -120,44 +120,44 @@ pub mod pallet {
QueryKind = OptionQuery,
>;
- /// TODO [6-genesis-builder]
+ /// [6-genesis-builder]
/// Keep track of latest UTXO hash of account
/// Mapping from `sr25519::Pubkey` to `BlakeTwo256::hash_of(transaction, index)`
/// Just for testing 🫤
/// Because 1 account may have multiple UTXOs
- // #[pallet::storage]
- // #[pallet::getter(fn utxo_of)]
- // pub type UtxoOf<T: Config> =
- // StorageMap<Hasher = Identity, Key = Public, Value = H256, QueryKind = OptionQuery>;
-
- /// TODO [6-genesis-builder]
- // #[pallet::genesis_config]
- // pub struct GenesisConfig<T: Config> {
- // pub _ph_data: Option<PhantomData<T>>,
- // pub genesis_utxos: Vec<GenesisUtxoType>,
- // }
-
- /// TODO [6-genesis-builder]
- // impl<T: Config> Default for GenesisConfig<T> {
- // fn default() -> Self {
- // Self {
- // _ph_data: Default::default(),
- // genesis_utxos: Vec::<GenesisUtxoType>::new(),
- // }
- // }
- // }
-
- /// TODO [6-genesis-builder]
- // #[pallet::genesis_build]
- // impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
- // fn build(&self) {
- // for utxo in self.genesis_utxos.iter() {
- // let utxo = TransactionOutput { value: utxo.0, pubkey: utxo.1 };
- // let hash = BlakeTwo256::hash_of(&utxo);
- // Pallet::<T>::store_utxo(&utxo, hash);
- // }
- // }
- // }
+ #[pallet::storage]
+ #[pallet::getter(fn utxo_of)]
+ pub type UtxoOf<T: Config> =
+ StorageMap<Hasher = Identity, Key = Public, Value = H256, QueryKind = OptionQuery>;
+
+ /// [6-genesis-builder]
+ #[pallet::genesis_config]
+ pub struct GenesisConfig<T: Config> {
+ pub _ph_data: Option<PhantomData<T>>,
+ pub genesis_utxos: Vec<GenesisUtxoType>,
+ }
+
+ /// [6-genesis-builder]
+ impl<T: Config> Default for GenesisConfig<T> {
+ fn default() -> Self {
+ Self {
+ _ph_data: Default::default(),
+ genesis_utxos: Vec::<GenesisUtxoType>::new(),
+ }
+ }
+ }
+
+ /// [6-genesis-builder]
+ #[pallet::genesis_build]
+ impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
+ fn build(&self) {
+ for utxo in self.genesis_utxos.iter() {
+ let utxo = TransactionOutput { value: utxo.0, pubkey: utxo.1 };
+ let hash = BlakeTwo256::hash_of(&utxo);
+ Pallet::<T>::store_utxo(&utxo, hash);
+ }
+ }
+ }
/// [2-data-structure]
/// Pallets use events to inform users when important changes are made.
@@ -303,10 +303,10 @@ pub mod pallet {
// [3-spend-utxo]
UtxoStore::<T>::insert(hash, utxo);
- // TODO [6-genesis-builder]
+ // [6-genesis-builder]
// Convert H256 back to sr25519::Public
- // let pubkey = Public::from_h256(utxo.pubkey);
- // UtxoOf::<T>::insert(pubkey, hash);
+ let pubkey = Public::from_h256(utxo.pubkey);
+ UtxoOf::<T>::insert(pubkey, hash);
}
/// Strips a transaction of its Signature fields by replacing value with ZERO-initialized fixed hash.
Client script
In this section, we will write a client script to generate sigscript
for UTXO transaction using Javascript and @polkadot/api
.
Initialize Working Environment
In this step, we will initialize a basic Javascript project, where we can start building our simple client script.
Make sure you have installed Node.js in your machine.
NPM init
- Create a new directory for your project:
mkdir scripts/generate-signature
cd scripts/generate-signature
- In that folder, run the following command to initialize a new Node.js project:
npm init -y
You'll see the package.json
file created in your project directory. This file contains metadata about your project, such as its name, version, description, author, and dependencies.
- Install necessary dependencies
npm install @polkadot/api @polkadot/keyring @polkadot/types @polkadot/util @polkadot/wasm-crypto
{
"name": "generate-signature",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "generate-signature",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"@polkadot/api": "^15.5.2",
"@polkadot/keyring": "^13.3.1",
"@polkadot/types": "^15.5.2",
"@polkadot/util": "^13.3.1",
"@polkadot/wasm-crypto": "^7.4.1"
}
},
"node_modules/@noble/curves": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz",
"integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==",
"license": "MIT",
"dependencies": {
"@noble/hashes": "1.7.1"
},
"engines": {
"node": "^14.21.3 || >=16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@noble/hashes": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz",
"integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==",
"license": "MIT",
"engines": {
"node": "^14.21.3 || >=16"
},
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@polkadot-api/json-rpc-provider": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.1.tgz",
"integrity": "sha512-/SMC/l7foRjpykLTUTacIH05H3mr9ip8b5xxfwXlVezXrNVLp3Cv0GX6uItkKd+ZjzVPf3PFrDF2B2/HLSNESA==",
"license": "MIT",
"optional": true
},
"node_modules/@polkadot-api/json-rpc-provider-proxy": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.1.0.tgz",
"integrity": "sha512-8GSFE5+EF73MCuLQm8tjrbCqlgclcHBSRaswvXziJ0ZW7iw3UEMsKkkKvELayWyBuOPa2T5i1nj6gFOeIsqvrg==",
"license": "MIT",
"optional": true
},
"node_modules/@polkadot-api/metadata-builders": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.3.2.tgz",
"integrity": "sha512-TKpfoT6vTb+513KDzMBTfCb/ORdgRnsS3TDFpOhAhZ08ikvK+hjHMt5plPiAX/OWkm1Wc9I3+K6W0hX5Ab7MVg==",
"license": "MIT",
"optional": true,
"dependencies": {
"@polkadot-api/substrate-bindings": "0.6.0",
"@polkadot-api/utils": "0.1.0"
}
},
"node_modules/@polkadot-api/observable-client": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.3.2.tgz",
"integrity": "sha512-HGgqWgEutVyOBXoGOPp4+IAq6CNdK/3MfQJmhCJb8YaJiaK4W6aRGrdQuQSTPHfERHCARt9BrOmEvTXAT257Ug==",
"license": "MIT",
"optional": true,
"dependencies": {
"@polkadot-api/metadata-builders": "0.3.2",
"@polkadot-api/substrate-bindings": "0.6.0",
"@polkadot-api/utils": "0.1.0"
},
"peerDependencies": {
"@polkadot-api/substrate-client": "0.1.4",
"rxjs": ">=7.8.0"
}
},
"node_modules/@polkadot-api/substrate-bindings": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.6.0.tgz",
"integrity": "sha512-lGuhE74NA1/PqdN7fKFdE5C1gNYX357j1tWzdlPXI0kQ7h3kN0zfxNOpPUN7dIrPcOFZ6C0tRRVrBylXkI6xPw==",
"license": "MIT",
"optional": true,
"dependencies": {
"@noble/hashes": "^1.3.1",
"@polkadot-api/utils": "0.1.0",
"@scure/base": "^1.1.1",
"scale-ts": "^1.6.0"
}
},
"node_modules/@polkadot-api/substrate-client": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.1.4.tgz",
"integrity": "sha512-MljrPobN0ZWTpn++da9vOvt+Ex+NlqTlr/XT7zi9sqPtDJiQcYl+d29hFAgpaeTqbeQKZwz3WDE9xcEfLE8c5A==",
"license": "MIT",
"optional": true,
"dependencies": {
"@polkadot-api/json-rpc-provider": "0.0.1",
"@polkadot-api/utils": "0.1.0"
}
},
"node_modules/@polkadot-api/utils": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.1.0.tgz",
"integrity": "sha512-MXzWZeuGxKizPx2Xf/47wx9sr/uxKw39bVJUptTJdsaQn/TGq+z310mHzf1RCGvC1diHM8f593KrnDgc9oNbJA==",
"license": "MIT",
"optional": true
},
"node_modules/@polkadot/api": {
"version": "15.5.2",
"resolved": "https://registry.npmjs.org/@polkadot/api/-/api-15.5.2.tgz",
"integrity": "sha512-TZPJlnoYr5kAfINooSmbvUcUCQnKDmTdLKNJmMh7CvCOYqQSJjFnMyfxhjM1bBTIcqnP9nFIGkldy19ErZHtcQ==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/api-augment": "15.5.2",
"@polkadot/api-base": "15.5.2",
"@polkadot/api-derive": "15.5.2",
"@polkadot/keyring": "^13.3.1",
"@polkadot/rpc-augment": "15.5.2",
"@polkadot/rpc-core": "15.5.2",
"@polkadot/rpc-provider": "15.5.2",
"@polkadot/types": "15.5.2",
"@polkadot/types-augment": "15.5.2",
"@polkadot/types-codec": "15.5.2",
"@polkadot/types-create": "15.5.2",
"@polkadot/types-known": "15.5.2",
"@polkadot/util": "^13.3.1",
"@polkadot/util-crypto": "^13.3.1",
"eventemitter3": "^5.0.1",
"rxjs": "^7.8.1",
"tslib": "^2.8.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/api-augment": {
"version": "15.5.2",
"resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-15.5.2.tgz",
"integrity": "sha512-EuE13KeifPj6PBfR3qMtqCq+Clbly/VX5adh53ljw7XTtHt8MKTlvhTK9pO9WHnixL6hUVH1f/Ekuzt8ROhpAg==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/api-base": "15.5.2",
"@polkadot/rpc-augment": "15.5.2",
"@polkadot/types": "15.5.2",
"@polkadot/types-augment": "15.5.2",
"@polkadot/types-codec": "15.5.2",
"@polkadot/util": "^13.3.1",
"tslib": "^2.8.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/api-base": {
"version": "15.5.2",
"resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-15.5.2.tgz",
"integrity": "sha512-e8W2KRwn6pkI/JIOrOCT2RsVil072vxQ95X2HCqu5eMmn5vwyOHB2OaMWezXikMegnCF3XEDeIzSP/VxGWW+zA==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/rpc-core": "15.5.2",
"@polkadot/types": "15.5.2",
"@polkadot/util": "^13.3.1",
"rxjs": "^7.8.1",
"tslib": "^2.8.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/api-derive": {
"version": "15.5.2",
"resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-15.5.2.tgz",
"integrity": "sha512-eSSPE0O9LE9jsRJIBqrPfHa7vCjE3NR+3ATdwAKBwK9LUfeCEEWud7c53njW8wLMSOiczxcX3JS2J9Rb0q7HCA==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/api": "15.5.2",
"@polkadot/api-augment": "15.5.2",
"@polkadot/api-base": "15.5.2",
"@polkadot/rpc-core": "15.5.2",
"@polkadot/types": "15.5.2",
"@polkadot/types-codec": "15.5.2",
"@polkadot/util": "^13.3.1",
"@polkadot/util-crypto": "^13.3.1",
"rxjs": "^7.8.1",
"tslib": "^2.8.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/keyring": {
"version": "13.3.1",
"resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-13.3.1.tgz",
"integrity": "sha512-PT3uG9MqciPyoEz/f23RRMSlht77fo1hZaA1Vbcs1Rz7h7qFC0+7jFI9Ak30EJh9V0I2YugfzqAe3NjjyDxlvw==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/util": "13.3.1",
"@polkadot/util-crypto": "13.3.1",
"tslib": "^2.8.0"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@polkadot/util": "13.3.1",
"@polkadot/util-crypto": "13.3.1"
}
},
"node_modules/@polkadot/networks": {
"version": "13.3.1",
"resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-13.3.1.tgz",
"integrity": "sha512-g/0OmCMUrbbW4RQ/xajTYd2SMJvFKY4kmMvpxtNN57hWQpY7c5oDXSz57jGH2uwvcBWeDfaNokcS+9hJL1RBcA==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/util": "13.3.1",
"@substrate/ss58-registry": "^1.51.0",
"tslib": "^2.8.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/rpc-augment": {
"version": "15.5.2",
"resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-15.5.2.tgz",
"integrity": "sha512-eDCcmgrb/csOcav/RRBPFkOdPRvRXVYlksCGlQuzobYkPZfPz5tIKmgenip/5qnviR6gag6tK9ara+8kZ/7tpw==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/rpc-core": "15.5.2",
"@polkadot/types": "15.5.2",
"@polkadot/types-codec": "15.5.2",
"@polkadot/util": "^13.3.1",
"tslib": "^2.8.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/rpc-core": {
"version": "15.5.2",
"resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-15.5.2.tgz",
"integrity": "sha512-a4Xq3iEJvRA9gHE+QxMaIEOkgjuZUfW+uI3XLDtPgtxu2yF3hceyRl/K1m6E4AXR383VaxQ45wvDS1odPbmwaw==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/rpc-augment": "15.5.2",
"@polkadot/rpc-provider": "15.5.2",
"@polkadot/types": "15.5.2",
"@polkadot/util": "^13.3.1",
"rxjs": "^7.8.1",
"tslib": "^2.8.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/rpc-provider": {
"version": "15.5.2",
"resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-15.5.2.tgz",
"integrity": "sha512-LHDG4mY5moFdVyUuEyuF9hMVWgA+DoLeNYZjtOL88gSALpfjhBLt1Hjbm3R6VwvcxeVNTkteVGvCo0AlH123JQ==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/keyring": "^13.3.1",
"@polkadot/types": "15.5.2",
"@polkadot/types-support": "15.5.2",
"@polkadot/util": "^13.3.1",
"@polkadot/util-crypto": "^13.3.1",
"@polkadot/x-fetch": "^13.3.1",
"@polkadot/x-global": "^13.3.1",
"@polkadot/x-ws": "^13.3.1",
"eventemitter3": "^5.0.1",
"mock-socket": "^9.3.1",
"nock": "^13.5.5",
"tslib": "^2.8.1"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@substrate/connect": "0.8.11"
}
},
"node_modules/@polkadot/types": {
"version": "15.5.2",
"resolved": "https://registry.npmjs.org/@polkadot/types/-/types-15.5.2.tgz",
"integrity": "sha512-TANrhfovLgbG00ldFvZbOuwyzqibOOMAU9egGqnoFwz4x4vTNTBgNJRvQGF5w4+FhjcEepdxlLU/3yqFM98yzA==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/keyring": "^13.3.1",
"@polkadot/types-augment": "15.5.2",
"@polkadot/types-codec": "15.5.2",
"@polkadot/types-create": "15.5.2",
"@polkadot/util": "^13.3.1",
"@polkadot/util-crypto": "^13.3.1",
"rxjs": "^7.8.1",
"tslib": "^2.8.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/types-augment": {
"version": "15.5.2",
"resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-15.5.2.tgz",
"integrity": "sha512-Za5ifsiu2gSQkOBjwcCtCr5nz+PojsBoptdHKjMm0JHLgilNn0kNqriqRRrCiPkUO1656UXRQNCHpP6Vs8tncg==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/types": "15.5.2",
"@polkadot/types-codec": "15.5.2",
"@polkadot/util": "^13.3.1",
"tslib": "^2.8.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/types-codec": {
"version": "15.5.2",
"resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-15.5.2.tgz",
"integrity": "sha512-caQNGQ+QHVZV4hGSolYN9L9pMRZeo9rJzYoybT5LMIxX8QVdW3RM5HDesj1SKGSrgIJmooN4/5JzPjrFZ4C7Vw==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/util": "^13.3.1",
"@polkadot/x-bigint": "^13.3.1",
"tslib": "^2.8.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/types-create": {
"version": "15.5.2",
"resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-15.5.2.tgz",
"integrity": "sha512-PbrT2mP5x7VTpwCZa/BCl8jl3y/s6UOwrjBL+pq8TErLEbkackn5a3cR4GaH9toeP5qlEaLbSWRFm24YcW8FOg==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/types-codec": "15.5.2",
"@polkadot/util": "^13.3.1",
"tslib": "^2.8.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/types-known": {
"version": "15.5.2",
"resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-15.5.2.tgz",
"integrity": "sha512-4yk1DnIeAy4uImFf4yDyRRdgkf5nAagQzPZWO61uxhEA+Q2MxLHLDeLbUwFiLxLLvpM09Xke8rGbem/vPRg9nA==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/networks": "^13.3.1",
"@polkadot/types": "15.5.2",
"@polkadot/types-codec": "15.5.2",
"@polkadot/types-create": "15.5.2",
"@polkadot/util": "^13.3.1",
"tslib": "^2.8.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/types-support": {
"version": "15.5.2",
"resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-15.5.2.tgz",
"integrity": "sha512-PT3MTSy69euAPtRQ3c2I7vM0OfAIQXU1yIWpYTcEkG+spTn4d0fcYeJjfxvAmcROJlpJoXf10YMsXKwx+gvOig==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/util": "^13.3.1",
"tslib": "^2.8.1"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/util": {
"version": "13.3.1",
"resolved": "https://registry.npmjs.org/@polkadot/util/-/util-13.3.1.tgz",
"integrity": "sha512-5crLP/rUZOJzuo/W8t73J8PxpibJ5vrxY57rR6V+mIpCZd1ORiw0wxeHcV5F9Adpn7yJyuGBwxPbueNR5Rr1Zw==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/x-bigint": "13.3.1",
"@polkadot/x-global": "13.3.1",
"@polkadot/x-textdecoder": "13.3.1",
"@polkadot/x-textencoder": "13.3.1",
"@types/bn.js": "^5.1.6",
"bn.js": "^5.2.1",
"tslib": "^2.8.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/util-crypto": {
"version": "13.3.1",
"resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.3.1.tgz",
"integrity": "sha512-FU6yf3IY++DKlf0eqO9/obe2y1zuZ5rbqRs75fyOME/5VXio1fA3GIpW7aFphyneFRd78G8QLh8kn0oIwBGMNg==",
"license": "Apache-2.0",
"dependencies": {
"@noble/curves": "^1.3.0",
"@noble/hashes": "^1.3.3",
"@polkadot/networks": "13.3.1",
"@polkadot/util": "13.3.1",
"@polkadot/wasm-crypto": "^7.4.1",
"@polkadot/wasm-util": "^7.4.1",
"@polkadot/x-bigint": "13.3.1",
"@polkadot/x-randomvalues": "13.3.1",
"@scure/base": "^1.1.7",
"tslib": "^2.8.0"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@polkadot/util": "13.3.1"
}
},
"node_modules/@polkadot/wasm-bridge": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.4.1.tgz",
"integrity": "sha512-tdkJaV453tezBxhF39r4oeG0A39sPKGDJmN81LYLf+Fihb7astzwju+u75BRmDrHZjZIv00un3razJEWCxze6g==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/wasm-util": "7.4.1",
"tslib": "^2.7.0"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@polkadot/util": "*",
"@polkadot/x-randomvalues": "*"
}
},
"node_modules/@polkadot/wasm-crypto": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.4.1.tgz",
"integrity": "sha512-kHN/kF7hYxm1y0WeFLWeWir6oTzvcFmR4N8fJJokR+ajYbdmrafPN+6iLgQVbhZnDdxyv9jWDuRRsDnBx8tPMQ==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/wasm-bridge": "7.4.1",
"@polkadot/wasm-crypto-asmjs": "7.4.1",
"@polkadot/wasm-crypto-init": "7.4.1",
"@polkadot/wasm-crypto-wasm": "7.4.1",
"@polkadot/wasm-util": "7.4.1",
"tslib": "^2.7.0"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@polkadot/util": "*",
"@polkadot/x-randomvalues": "*"
}
},
"node_modules/@polkadot/wasm-crypto-asmjs": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.4.1.tgz",
"integrity": "sha512-pwU8QXhUW7IberyHJIQr37IhbB6DPkCG5FhozCiNTq4vFBsFPjm9q8aZh7oX1QHQaiAZa2m2/VjIVE+FHGbvHQ==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.7.0"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@polkadot/util": "*"
}
},
"node_modules/@polkadot/wasm-crypto-init": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.4.1.tgz",
"integrity": "sha512-AVka33+f7MvXEEIGq5U0dhaA2SaXMXnxVCQyhJTaCnJ5bRDj0Xlm3ijwDEQUiaDql7EikbkkRtmlvs95eSUWYQ==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/wasm-bridge": "7.4.1",
"@polkadot/wasm-crypto-asmjs": "7.4.1",
"@polkadot/wasm-crypto-wasm": "7.4.1",
"@polkadot/wasm-util": "7.4.1",
"tslib": "^2.7.0"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@polkadot/util": "*",
"@polkadot/x-randomvalues": "*"
}
},
"node_modules/@polkadot/wasm-crypto-wasm": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.4.1.tgz",
"integrity": "sha512-PE1OAoupFR0ZOV2O8tr7D1FEUAwaggzxtfs3Aa5gr+yxlSOaWUKeqsOYe1KdrcjmZVV3iINEAXxgrbzCmiuONg==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/wasm-util": "7.4.1",
"tslib": "^2.7.0"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@polkadot/util": "*"
}
},
"node_modules/@polkadot/wasm-util": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.4.1.tgz",
"integrity": "sha512-RAcxNFf3zzpkr+LX/ItAsvj+QyM56TomJ0xjUMo4wKkHjwsxkz4dWJtx5knIgQz/OthqSDMR59VNEycQeNuXzA==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.7.0"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@polkadot/util": "*"
}
},
"node_modules/@polkadot/x-bigint": {
"version": "13.3.1",
"resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-13.3.1.tgz",
"integrity": "sha512-ewc708a7LUdrT92v9DsSAIbcJQBn3aR9/LavF/iyMOq5lZJyPXDSjAnskfMs818R3RLCrKVKfs+aKkxt2eqo8g==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/x-global": "13.3.1",
"tslib": "^2.8.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/x-fetch": {
"version": "13.3.1",
"resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-13.3.1.tgz",
"integrity": "sha512-J+HM42j0KGqdC/eo7vmsdLPz74MR7+0My4km6TG9HGjKqqztwygtenpopPod2SbRnL4nHiEG0wZzpVOW6HN2gw==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/x-global": "13.3.1",
"node-fetch": "^3.3.2",
"tslib": "^2.8.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/x-global": {
"version": "13.3.1",
"resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-13.3.1.tgz",
"integrity": "sha512-861TeIw49a3JvkwlUWrddfG+JaUqtFZDsemYxxZIjjcRJLrKOsoKNqHbiHi2OPrwlX8PwAA/wc5I9Q4XRQ7KEg==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.8.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/x-randomvalues": {
"version": "13.3.1",
"resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-13.3.1.tgz",
"integrity": "sha512-GIb0au3vIX2U/DRH0PRckM+1I4EIbU8PLX1roGJgN1MAYKWiylJTKPVoBMafMM87o8qauOevJ46uYB/qlfbiWg==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/x-global": "13.3.1",
"tslib": "^2.8.0"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@polkadot/util": "13.3.1",
"@polkadot/wasm-util": "*"
}
},
"node_modules/@polkadot/x-textdecoder": {
"version": "13.3.1",
"resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-13.3.1.tgz",
"integrity": "sha512-g2R9O1p0ZsNDhZ3uEBZh6fQaVLlo3yFr0YNqt15v7e9lBI4APvTJ202EINlo2jB5lz/R438/BdjEA3AL+0zUtQ==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/x-global": "13.3.1",
"tslib": "^2.8.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/x-textencoder": {
"version": "13.3.1",
"resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-13.3.1.tgz",
"integrity": "sha512-DnHLUdoKDYxekfxopuUuPB+j5Mu7Jemejcduu5gz3/89GP/sYPAu0CAVbq9B+hK1yGjBBj31eA4wkAV1oktYmg==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/x-global": "13.3.1",
"tslib": "^2.8.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@polkadot/x-ws": {
"version": "13.3.1",
"resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-13.3.1.tgz",
"integrity": "sha512-ytqkC7FwVs4BlzNFAmPMFp+xD1KIdMMP/mvCSOrnxjlsyM5DVGop4x4c2ZgDUBmrFqmIiVkWDfMIZeOxui2OLQ==",
"license": "Apache-2.0",
"dependencies": {
"@polkadot/x-global": "13.3.1",
"tslib": "^2.8.0",
"ws": "^8.18.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@scure/base": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz",
"integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==",
"license": "MIT",
"funding": {
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@substrate/connect": {
"version": "0.8.11",
"resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.8.11.tgz",
"integrity": "sha512-ofLs1PAO9AtDdPbdyTYj217Pe+lBfTLltdHDs3ds8no0BseoLeAGxpz1mHfi7zB4IxI3YyAiLjH6U8cw4pj4Nw==",
"deprecated": "versions below 1.x are no longer maintained",
"license": "GPL-3.0-only",
"optional": true,
"dependencies": {
"@substrate/connect-extension-protocol": "^2.0.0",
"@substrate/connect-known-chains": "^1.1.5",
"@substrate/light-client-extension-helpers": "^1.0.0",
"smoldot": "2.0.26"
}
},
"node_modules/@substrate/connect-extension-protocol": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-2.2.1.tgz",
"integrity": "sha512-GoafTgm/Jey9E4Xlj4Z5ZBt/H4drH2CNq8VrAro80rtoznrXnFDNVivLQzZN0Xaj2g8YXSn9pC9Oc9IovYZJXw==",
"license": "GPL-3.0-only",
"optional": true
},
"node_modules/@substrate/connect-known-chains": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/@substrate/connect-known-chains/-/connect-known-chains-1.9.1.tgz",
"integrity": "sha512-dWNf5x3hjrY4s+WEovPKL0jH58pyIaIiAM918aAdnp/VKkAMqOfhKzRWL7BqJDKCm95bL4fqnOfaXZ3SQpVoYw==",
"license": "GPL-3.0-only",
"optional": true
},
"node_modules/@substrate/light-client-extension-helpers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@substrate/light-client-extension-helpers/-/light-client-extension-helpers-1.0.0.tgz",
"integrity": "sha512-TdKlni1mBBZptOaeVrKnusMg/UBpWUORNDv5fdCaJklP4RJiFOzBCrzC+CyVI5kQzsXBisZ+2pXm+rIjS38kHg==",
"license": "MIT",
"optional": true,
"dependencies": {
"@polkadot-api/json-rpc-provider": "^0.0.1",
"@polkadot-api/json-rpc-provider-proxy": "^0.1.0",
"@polkadot-api/observable-client": "^0.3.0",
"@polkadot-api/substrate-client": "^0.1.2",
"@substrate/connect-extension-protocol": "^2.0.0",
"@substrate/connect-known-chains": "^1.1.5",
"rxjs": "^7.8.1"
},
"peerDependencies": {
"smoldot": "2.x"
}
},
"node_modules/@substrate/ss58-registry": {
"version": "1.51.0",
"resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.51.0.tgz",
"integrity": "sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ==",
"license": "Apache-2.0"
},
"node_modules/@types/bn.js": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.6.tgz",
"integrity": "sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/node": {
"version": "22.13.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz",
"integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.20.0"
}
},
"node_modules/bn.js": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
"integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==",
"license": "MIT"
},
"node_modules/data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
"integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
"license": "MIT",
"engines": {
"node": ">= 12"
}
},
"node_modules/debug": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/eventemitter3": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
"license": "MIT"
},
"node_modules/fetch-blob": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
"integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "paypal",
"url": "https://paypal.me/jimmywarting"
}
],
"license": "MIT",
"dependencies": {
"node-domexception": "^1.0.0",
"web-streams-polyfill": "^3.0.3"
},
"engines": {
"node": "^12.20 || >= 14.13"
}
},
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
"integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
"license": "MIT",
"dependencies": {
"fetch-blob": "^3.1.2"
},
"engines": {
"node": ">=12.20.0"
}
},
"node_modules/json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
"license": "ISC"
},
"node_modules/mock-socket": {
"version": "9.3.1",
"resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz",
"integrity": "sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==",
"license": "MIT",
"engines": {
"node": ">= 8"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/nock": {
"version": "13.5.6",
"resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz",
"integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==",
"license": "MIT",
"dependencies": {
"debug": "^4.1.0",
"json-stringify-safe": "^5.0.1",
"propagate": "^2.0.0"
},
"engines": {
"node": ">= 10.13"
}
},
"node_modules/node-domexception": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
"integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/jimmywarting"
},
{
"type": "github",
"url": "https://paypal.me/jimmywarting"
}
],
"license": "MIT",
"engines": {
"node": ">=10.5.0"
}
},
"node_modules/node-fetch": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
"integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
"license": "MIT",
"dependencies": {
"data-uri-to-buffer": "^4.0.0",
"fetch-blob": "^3.1.4",
"formdata-polyfill": "^4.0.10"
},
"engines": {
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-fetch"
}
},
"node_modules/propagate": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz",
"integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==",
"license": "MIT",
"engines": {
"node": ">= 8"
}
},
"node_modules/rxjs": {
"version": "7.8.1",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/scale-ts": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/scale-ts/-/scale-ts-1.6.1.tgz",
"integrity": "sha512-PBMc2AWc6wSEqJYBDPcyCLUj9/tMKnLX70jLOSndMtcUoLQucP/DM0vnQo1wJAYjTrQiq8iG9rD0q6wFzgjH7g==",
"license": "MIT",
"optional": true
},
"node_modules/smoldot": {
"version": "2.0.26",
"resolved": "https://registry.npmjs.org/smoldot/-/smoldot-2.0.26.tgz",
"integrity": "sha512-F+qYmH4z2s2FK+CxGj8moYcd1ekSIKH8ywkdqlOz88Dat35iB1DIYL11aILN46YSGMzQW/lbJNS307zBSDN5Ig==",
"license": "GPL-3.0-or-later WITH Classpath-exception-2.0",
"optional": true,
"dependencies": {
"ws": "^8.8.1"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/undici-types": {
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
"license": "MIT"
},
"node_modules/web-streams-polyfill": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
"integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
"license": "MIT",
"engines": {
"node": ">= 8"
}
},
"node_modules/ws": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
}
}
{
"name": "generate-signature",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"@polkadot/api": "^15.5.2",
"@polkadot/keyring": "^13.3.1",
"@polkadot/types": "^15.5.2",
"@polkadot/util": "^13.3.1",
"@polkadot/wasm-crypto": "^7.4.1"
}
}
diff --git a/scripts/generate-signature/package-lock.json b/scripts/generate-signature/package-lock.json
new file mode 100644
index 0000000..86de335
--- /dev/null
+++ b/scripts/generate-signature/package-lock.json
@@ -0,0 +1,929 @@
+{
+ "name": "generate-signature",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "generate-signature",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "@polkadot/api": "^15.5.2",
+ "@polkadot/keyring": "^13.3.1",
+ "@polkadot/types": "^15.5.2",
+ "@polkadot/util": "^13.3.1",
+ "@polkadot/wasm-crypto": "^7.4.1"
+ }
+ },
+ "node_modules/@noble/curves": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.1.tgz",
+ "integrity": "sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@noble/hashes": "1.7.1"
+ },
+ "engines": {
+ "node": "^14.21.3 || >=16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@noble/hashes": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz",
+ "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==",
+ "license": "MIT",
+ "engines": {
+ "node": "^14.21.3 || >=16"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@polkadot-api/json-rpc-provider": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.1.tgz",
+ "integrity": "sha512-/SMC/l7foRjpykLTUTacIH05H3mr9ip8b5xxfwXlVezXrNVLp3Cv0GX6uItkKd+ZjzVPf3PFrDF2B2/HLSNESA==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@polkadot-api/json-rpc-provider-proxy": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.1.0.tgz",
+ "integrity": "sha512-8GSFE5+EF73MCuLQm8tjrbCqlgclcHBSRaswvXziJ0ZW7iw3UEMsKkkKvELayWyBuOPa2T5i1nj6gFOeIsqvrg==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@polkadot-api/metadata-builders": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.3.2.tgz",
+ "integrity": "sha512-TKpfoT6vTb+513KDzMBTfCb/ORdgRnsS3TDFpOhAhZ08ikvK+hjHMt5plPiAX/OWkm1Wc9I3+K6W0hX5Ab7MVg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@polkadot-api/substrate-bindings": "0.6.0",
+ "@polkadot-api/utils": "0.1.0"
+ }
+ },
+ "node_modules/@polkadot-api/observable-client": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.3.2.tgz",
+ "integrity": "sha512-HGgqWgEutVyOBXoGOPp4+IAq6CNdK/3MfQJmhCJb8YaJiaK4W6aRGrdQuQSTPHfERHCARt9BrOmEvTXAT257Ug==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@polkadot-api/metadata-builders": "0.3.2",
+ "@polkadot-api/substrate-bindings": "0.6.0",
+ "@polkadot-api/utils": "0.1.0"
+ },
+ "peerDependencies": {
+ "@polkadot-api/substrate-client": "0.1.4",
+ "rxjs": ">=7.8.0"
+ }
+ },
+ "node_modules/@polkadot-api/substrate-bindings": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.6.0.tgz",
+ "integrity": "sha512-lGuhE74NA1/PqdN7fKFdE5C1gNYX357j1tWzdlPXI0kQ7h3kN0zfxNOpPUN7dIrPcOFZ6C0tRRVrBylXkI6xPw==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@noble/hashes": "^1.3.1",
+ "@polkadot-api/utils": "0.1.0",
+ "@scure/base": "^1.1.1",
+ "scale-ts": "^1.6.0"
+ }
+ },
+ "node_modules/@polkadot-api/substrate-client": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.1.4.tgz",
+ "integrity": "sha512-MljrPobN0ZWTpn++da9vOvt+Ex+NlqTlr/XT7zi9sqPtDJiQcYl+d29hFAgpaeTqbeQKZwz3WDE9xcEfLE8c5A==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@polkadot-api/json-rpc-provider": "0.0.1",
+ "@polkadot-api/utils": "0.1.0"
+ }
+ },
+ "node_modules/@polkadot-api/utils": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.1.0.tgz",
+ "integrity": "sha512-MXzWZeuGxKizPx2Xf/47wx9sr/uxKw39bVJUptTJdsaQn/TGq+z310mHzf1RCGvC1diHM8f593KrnDgc9oNbJA==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@polkadot/api": {
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-15.5.2.tgz",
+ "integrity": "sha512-TZPJlnoYr5kAfINooSmbvUcUCQnKDmTdLKNJmMh7CvCOYqQSJjFnMyfxhjM1bBTIcqnP9nFIGkldy19ErZHtcQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/api-augment": "15.5.2",
+ "@polkadot/api-base": "15.5.2",
+ "@polkadot/api-derive": "15.5.2",
+ "@polkadot/keyring": "^13.3.1",
+ "@polkadot/rpc-augment": "15.5.2",
+ "@polkadot/rpc-core": "15.5.2",
+ "@polkadot/rpc-provider": "15.5.2",
+ "@polkadot/types": "15.5.2",
+ "@polkadot/types-augment": "15.5.2",
+ "@polkadot/types-codec": "15.5.2",
+ "@polkadot/types-create": "15.5.2",
+ "@polkadot/types-known": "15.5.2",
+ "@polkadot/util": "^13.3.1",
+ "@polkadot/util-crypto": "^13.3.1",
+ "eventemitter3": "^5.0.1",
+ "rxjs": "^7.8.1",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/api-augment": {
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-15.5.2.tgz",
+ "integrity": "sha512-EuE13KeifPj6PBfR3qMtqCq+Clbly/VX5adh53ljw7XTtHt8MKTlvhTK9pO9WHnixL6hUVH1f/Ekuzt8ROhpAg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/api-base": "15.5.2",
+ "@polkadot/rpc-augment": "15.5.2",
+ "@polkadot/types": "15.5.2",
+ "@polkadot/types-augment": "15.5.2",
+ "@polkadot/types-codec": "15.5.2",
+ "@polkadot/util": "^13.3.1",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/api-base": {
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-15.5.2.tgz",
+ "integrity": "sha512-e8W2KRwn6pkI/JIOrOCT2RsVil072vxQ95X2HCqu5eMmn5vwyOHB2OaMWezXikMegnCF3XEDeIzSP/VxGWW+zA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/rpc-core": "15.5.2",
+ "@polkadot/types": "15.5.2",
+ "@polkadot/util": "^13.3.1",
+ "rxjs": "^7.8.1",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/api-derive": {
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-15.5.2.tgz",
+ "integrity": "sha512-eSSPE0O9LE9jsRJIBqrPfHa7vCjE3NR+3ATdwAKBwK9LUfeCEEWud7c53njW8wLMSOiczxcX3JS2J9Rb0q7HCA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/api": "15.5.2",
+ "@polkadot/api-augment": "15.5.2",
+ "@polkadot/api-base": "15.5.2",
+ "@polkadot/rpc-core": "15.5.2",
+ "@polkadot/types": "15.5.2",
+ "@polkadot/types-codec": "15.5.2",
+ "@polkadot/util": "^13.3.1",
+ "@polkadot/util-crypto": "^13.3.1",
+ "rxjs": "^7.8.1",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/keyring": {
+ "version": "13.3.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-13.3.1.tgz",
+ "integrity": "sha512-PT3uG9MqciPyoEz/f23RRMSlht77fo1hZaA1Vbcs1Rz7h7qFC0+7jFI9Ak30EJh9V0I2YugfzqAe3NjjyDxlvw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/util": "13.3.1",
+ "@polkadot/util-crypto": "13.3.1",
+ "tslib": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@polkadot/util": "13.3.1",
+ "@polkadot/util-crypto": "13.3.1"
+ }
+ },
+ "node_modules/@polkadot/networks": {
+ "version": "13.3.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-13.3.1.tgz",
+ "integrity": "sha512-g/0OmCMUrbbW4RQ/xajTYd2SMJvFKY4kmMvpxtNN57hWQpY7c5oDXSz57jGH2uwvcBWeDfaNokcS+9hJL1RBcA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/util": "13.3.1",
+ "@substrate/ss58-registry": "^1.51.0",
+ "tslib": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/rpc-augment": {
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-15.5.2.tgz",
+ "integrity": "sha512-eDCcmgrb/csOcav/RRBPFkOdPRvRXVYlksCGlQuzobYkPZfPz5tIKmgenip/5qnviR6gag6tK9ara+8kZ/7tpw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/rpc-core": "15.5.2",
+ "@polkadot/types": "15.5.2",
+ "@polkadot/types-codec": "15.5.2",
+ "@polkadot/util": "^13.3.1",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/rpc-core": {
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-15.5.2.tgz",
+ "integrity": "sha512-a4Xq3iEJvRA9gHE+QxMaIEOkgjuZUfW+uI3XLDtPgtxu2yF3hceyRl/K1m6E4AXR383VaxQ45wvDS1odPbmwaw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/rpc-augment": "15.5.2",
+ "@polkadot/rpc-provider": "15.5.2",
+ "@polkadot/types": "15.5.2",
+ "@polkadot/util": "^13.3.1",
+ "rxjs": "^7.8.1",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/rpc-provider": {
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-15.5.2.tgz",
+ "integrity": "sha512-LHDG4mY5moFdVyUuEyuF9hMVWgA+DoLeNYZjtOL88gSALpfjhBLt1Hjbm3R6VwvcxeVNTkteVGvCo0AlH123JQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/keyring": "^13.3.1",
+ "@polkadot/types": "15.5.2",
+ "@polkadot/types-support": "15.5.2",
+ "@polkadot/util": "^13.3.1",
+ "@polkadot/util-crypto": "^13.3.1",
+ "@polkadot/x-fetch": "^13.3.1",
+ "@polkadot/x-global": "^13.3.1",
+ "@polkadot/x-ws": "^13.3.1",
+ "eventemitter3": "^5.0.1",
+ "mock-socket": "^9.3.1",
+ "nock": "^13.5.5",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@substrate/connect": "0.8.11"
+ }
+ },
+ "node_modules/@polkadot/types": {
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-15.5.2.tgz",
+ "integrity": "sha512-TANrhfovLgbG00ldFvZbOuwyzqibOOMAU9egGqnoFwz4x4vTNTBgNJRvQGF5w4+FhjcEepdxlLU/3yqFM98yzA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/keyring": "^13.3.1",
+ "@polkadot/types-augment": "15.5.2",
+ "@polkadot/types-codec": "15.5.2",
+ "@polkadot/types-create": "15.5.2",
+ "@polkadot/util": "^13.3.1",
+ "@polkadot/util-crypto": "^13.3.1",
+ "rxjs": "^7.8.1",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/types-augment": {
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-15.5.2.tgz",
+ "integrity": "sha512-Za5ifsiu2gSQkOBjwcCtCr5nz+PojsBoptdHKjMm0JHLgilNn0kNqriqRRrCiPkUO1656UXRQNCHpP6Vs8tncg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/types": "15.5.2",
+ "@polkadot/types-codec": "15.5.2",
+ "@polkadot/util": "^13.3.1",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/types-codec": {
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-15.5.2.tgz",
+ "integrity": "sha512-caQNGQ+QHVZV4hGSolYN9L9pMRZeo9rJzYoybT5LMIxX8QVdW3RM5HDesj1SKGSrgIJmooN4/5JzPjrFZ4C7Vw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/util": "^13.3.1",
+ "@polkadot/x-bigint": "^13.3.1",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/types-create": {
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-15.5.2.tgz",
+ "integrity": "sha512-PbrT2mP5x7VTpwCZa/BCl8jl3y/s6UOwrjBL+pq8TErLEbkackn5a3cR4GaH9toeP5qlEaLbSWRFm24YcW8FOg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/types-codec": "15.5.2",
+ "@polkadot/util": "^13.3.1",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/types-known": {
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-15.5.2.tgz",
+ "integrity": "sha512-4yk1DnIeAy4uImFf4yDyRRdgkf5nAagQzPZWO61uxhEA+Q2MxLHLDeLbUwFiLxLLvpM09Xke8rGbem/vPRg9nA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/networks": "^13.3.1",
+ "@polkadot/types": "15.5.2",
+ "@polkadot/types-codec": "15.5.2",
+ "@polkadot/types-create": "15.5.2",
+ "@polkadot/util": "^13.3.1",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/types-support": {
+ "version": "15.5.2",
+ "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-15.5.2.tgz",
+ "integrity": "sha512-PT3MTSy69euAPtRQ3c2I7vM0OfAIQXU1yIWpYTcEkG+spTn4d0fcYeJjfxvAmcROJlpJoXf10YMsXKwx+gvOig==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/util": "^13.3.1",
+ "tslib": "^2.8.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/util": {
+ "version": "13.3.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-13.3.1.tgz",
+ "integrity": "sha512-5crLP/rUZOJzuo/W8t73J8PxpibJ5vrxY57rR6V+mIpCZd1ORiw0wxeHcV5F9Adpn7yJyuGBwxPbueNR5Rr1Zw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/x-bigint": "13.3.1",
+ "@polkadot/x-global": "13.3.1",
+ "@polkadot/x-textdecoder": "13.3.1",
+ "@polkadot/x-textencoder": "13.3.1",
+ "@types/bn.js": "^5.1.6",
+ "bn.js": "^5.2.1",
+ "tslib": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/util-crypto": {
+ "version": "13.3.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.3.1.tgz",
+ "integrity": "sha512-FU6yf3IY++DKlf0eqO9/obe2y1zuZ5rbqRs75fyOME/5VXio1fA3GIpW7aFphyneFRd78G8QLh8kn0oIwBGMNg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@noble/curves": "^1.3.0",
+ "@noble/hashes": "^1.3.3",
+ "@polkadot/networks": "13.3.1",
+ "@polkadot/util": "13.3.1",
+ "@polkadot/wasm-crypto": "^7.4.1",
+ "@polkadot/wasm-util": "^7.4.1",
+ "@polkadot/x-bigint": "13.3.1",
+ "@polkadot/x-randomvalues": "13.3.1",
+ "@scure/base": "^1.1.7",
+ "tslib": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@polkadot/util": "13.3.1"
+ }
+ },
+ "node_modules/@polkadot/wasm-bridge": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.4.1.tgz",
+ "integrity": "sha512-tdkJaV453tezBxhF39r4oeG0A39sPKGDJmN81LYLf+Fihb7astzwju+u75BRmDrHZjZIv00un3razJEWCxze6g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/wasm-util": "7.4.1",
+ "tslib": "^2.7.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@polkadot/util": "*",
+ "@polkadot/x-randomvalues": "*"
+ }
+ },
+ "node_modules/@polkadot/wasm-crypto": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.4.1.tgz",
+ "integrity": "sha512-kHN/kF7hYxm1y0WeFLWeWir6oTzvcFmR4N8fJJokR+ajYbdmrafPN+6iLgQVbhZnDdxyv9jWDuRRsDnBx8tPMQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/wasm-bridge": "7.4.1",
+ "@polkadot/wasm-crypto-asmjs": "7.4.1",
+ "@polkadot/wasm-crypto-init": "7.4.1",
+ "@polkadot/wasm-crypto-wasm": "7.4.1",
+ "@polkadot/wasm-util": "7.4.1",
+ "tslib": "^2.7.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@polkadot/util": "*",
+ "@polkadot/x-randomvalues": "*"
+ }
+ },
+ "node_modules/@polkadot/wasm-crypto-asmjs": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.4.1.tgz",
+ "integrity": "sha512-pwU8QXhUW7IberyHJIQr37IhbB6DPkCG5FhozCiNTq4vFBsFPjm9q8aZh7oX1QHQaiAZa2m2/VjIVE+FHGbvHQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.7.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@polkadot/util": "*"
+ }
+ },
+ "node_modules/@polkadot/wasm-crypto-init": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.4.1.tgz",
+ "integrity": "sha512-AVka33+f7MvXEEIGq5U0dhaA2SaXMXnxVCQyhJTaCnJ5bRDj0Xlm3ijwDEQUiaDql7EikbkkRtmlvs95eSUWYQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/wasm-bridge": "7.4.1",
+ "@polkadot/wasm-crypto-asmjs": "7.4.1",
+ "@polkadot/wasm-crypto-wasm": "7.4.1",
+ "@polkadot/wasm-util": "7.4.1",
+ "tslib": "^2.7.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@polkadot/util": "*",
+ "@polkadot/x-randomvalues": "*"
+ }
+ },
+ "node_modules/@polkadot/wasm-crypto-wasm": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.4.1.tgz",
+ "integrity": "sha512-PE1OAoupFR0ZOV2O8tr7D1FEUAwaggzxtfs3Aa5gr+yxlSOaWUKeqsOYe1KdrcjmZVV3iINEAXxgrbzCmiuONg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/wasm-util": "7.4.1",
+ "tslib": "^2.7.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@polkadot/util": "*"
+ }
+ },
+ "node_modules/@polkadot/wasm-util": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.4.1.tgz",
+ "integrity": "sha512-RAcxNFf3zzpkr+LX/ItAsvj+QyM56TomJ0xjUMo4wKkHjwsxkz4dWJtx5knIgQz/OthqSDMR59VNEycQeNuXzA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.7.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@polkadot/util": "*"
+ }
+ },
+ "node_modules/@polkadot/x-bigint": {
+ "version": "13.3.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-13.3.1.tgz",
+ "integrity": "sha512-ewc708a7LUdrT92v9DsSAIbcJQBn3aR9/LavF/iyMOq5lZJyPXDSjAnskfMs818R3RLCrKVKfs+aKkxt2eqo8g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/x-global": "13.3.1",
+ "tslib": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/x-fetch": {
+ "version": "13.3.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-13.3.1.tgz",
+ "integrity": "sha512-J+HM42j0KGqdC/eo7vmsdLPz74MR7+0My4km6TG9HGjKqqztwygtenpopPod2SbRnL4nHiEG0wZzpVOW6HN2gw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/x-global": "13.3.1",
+ "node-fetch": "^3.3.2",
+ "tslib": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/x-global": {
+ "version": "13.3.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-13.3.1.tgz",
+ "integrity": "sha512-861TeIw49a3JvkwlUWrddfG+JaUqtFZDsemYxxZIjjcRJLrKOsoKNqHbiHi2OPrwlX8PwAA/wc5I9Q4XRQ7KEg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/x-randomvalues": {
+ "version": "13.3.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-13.3.1.tgz",
+ "integrity": "sha512-GIb0au3vIX2U/DRH0PRckM+1I4EIbU8PLX1roGJgN1MAYKWiylJTKPVoBMafMM87o8qauOevJ46uYB/qlfbiWg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/x-global": "13.3.1",
+ "tslib": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@polkadot/util": "13.3.1",
+ "@polkadot/wasm-util": "*"
+ }
+ },
+ "node_modules/@polkadot/x-textdecoder": {
+ "version": "13.3.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-13.3.1.tgz",
+ "integrity": "sha512-g2R9O1p0ZsNDhZ3uEBZh6fQaVLlo3yFr0YNqt15v7e9lBI4APvTJ202EINlo2jB5lz/R438/BdjEA3AL+0zUtQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/x-global": "13.3.1",
+ "tslib": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/x-textencoder": {
+ "version": "13.3.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-13.3.1.tgz",
+ "integrity": "sha512-DnHLUdoKDYxekfxopuUuPB+j5Mu7Jemejcduu5gz3/89GP/sYPAu0CAVbq9B+hK1yGjBBj31eA4wkAV1oktYmg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/x-global": "13.3.1",
+ "tslib": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@polkadot/x-ws": {
+ "version": "13.3.1",
+ "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-13.3.1.tgz",
+ "integrity": "sha512-ytqkC7FwVs4BlzNFAmPMFp+xD1KIdMMP/mvCSOrnxjlsyM5DVGop4x4c2ZgDUBmrFqmIiVkWDfMIZeOxui2OLQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@polkadot/x-global": "13.3.1",
+ "tslib": "^2.8.0",
+ "ws": "^8.18.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@scure/base": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.4.tgz",
+ "integrity": "sha512-5Yy9czTO47mqz+/J8GM6GIId4umdCk1wc1q8rKERQulIoc8VP9pzDcghv10Tl2E7R96ZUx/PhND3ESYUQX8NuQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@substrate/connect": {
+ "version": "0.8.11",
+ "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.8.11.tgz",
+ "integrity": "sha512-ofLs1PAO9AtDdPbdyTYj217Pe+lBfTLltdHDs3ds8no0BseoLeAGxpz1mHfi7zB4IxI3YyAiLjH6U8cw4pj4Nw==",
+ "deprecated": "versions below 1.x are no longer maintained",
+ "license": "GPL-3.0-only",
+ "optional": true,
+ "dependencies": {
+ "@substrate/connect-extension-protocol": "^2.0.0",
+ "@substrate/connect-known-chains": "^1.1.5",
+ "@substrate/light-client-extension-helpers": "^1.0.0",
+ "smoldot": "2.0.26"
+ }
+ },
+ "node_modules/@substrate/connect-extension-protocol": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-2.2.1.tgz",
+ "integrity": "sha512-GoafTgm/Jey9E4Xlj4Z5ZBt/H4drH2CNq8VrAro80rtoznrXnFDNVivLQzZN0Xaj2g8YXSn9pC9Oc9IovYZJXw==",
+ "license": "GPL-3.0-only",
+ "optional": true
+ },
+ "node_modules/@substrate/connect-known-chains": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/@substrate/connect-known-chains/-/connect-known-chains-1.9.1.tgz",
+ "integrity": "sha512-dWNf5x3hjrY4s+WEovPKL0jH58pyIaIiAM918aAdnp/VKkAMqOfhKzRWL7BqJDKCm95bL4fqnOfaXZ3SQpVoYw==",
+ "license": "GPL-3.0-only",
+ "optional": true
+ },
+ "node_modules/@substrate/light-client-extension-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@substrate/light-client-extension-helpers/-/light-client-extension-helpers-1.0.0.tgz",
+ "integrity": "sha512-TdKlni1mBBZptOaeVrKnusMg/UBpWUORNDv5fdCaJklP4RJiFOzBCrzC+CyVI5kQzsXBisZ+2pXm+rIjS38kHg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@polkadot-api/json-rpc-provider": "^0.0.1",
+ "@polkadot-api/json-rpc-provider-proxy": "^0.1.0",
+ "@polkadot-api/observable-client": "^0.3.0",
+ "@polkadot-api/substrate-client": "^0.1.2",
+ "@substrate/connect-extension-protocol": "^2.0.0",
+ "@substrate/connect-known-chains": "^1.1.5",
+ "rxjs": "^7.8.1"
+ },
+ "peerDependencies": {
+ "smoldot": "2.x"
+ }
+ },
+ "node_modules/@substrate/ss58-registry": {
+ "version": "1.51.0",
+ "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.51.0.tgz",
+ "integrity": "sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@types/bn.js": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.6.tgz",
+ "integrity": "sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "22.13.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz",
+ "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==",
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.20.0"
+ }
+ },
+ "node_modules/bn.js": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
+ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==",
+ "license": "MIT"
+ },
+ "node_modules/data-uri-to-buffer": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
+ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+ "license": "MIT"
+ },
+ "node_modules/fetch-blob": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "node-domexception": "^1.0.0",
+ "web-streams-polyfill": "^3.0.3"
+ },
+ "engines": {
+ "node": "^12.20 || >= 14.13"
+ }
+ },
+ "node_modules/formdata-polyfill": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+ "license": "MIT",
+ "dependencies": {
+ "fetch-blob": "^3.1.2"
+ },
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "license": "ISC"
+ },
+ "node_modules/mock-socket": {
+ "version": "9.3.1",
+ "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz",
+ "integrity": "sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/nock": {
+ "version": "13.5.6",
+ "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz",
+ "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.1.0",
+ "json-stringify-safe": "^5.0.1",
+ "propagate": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13"
+ }
+ },
+ "node_modules/node-domexception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "github",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.5.0"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+ "license": "MIT",
+ "dependencies": {
+ "data-uri-to-buffer": "^4.0.0",
+ "fetch-blob": "^3.1.4",
+ "formdata-polyfill": "^4.0.10"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/node-fetch"
+ }
+ },
+ "node_modules/propagate": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz",
+ "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/rxjs": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
+ "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/scale-ts": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/scale-ts/-/scale-ts-1.6.1.tgz",
+ "integrity": "sha512-PBMc2AWc6wSEqJYBDPcyCLUj9/tMKnLX70jLOSndMtcUoLQucP/DM0vnQo1wJAYjTrQiq8iG9rD0q6wFzgjH7g==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/smoldot": {
+ "version": "2.0.26",
+ "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-2.0.26.tgz",
+ "integrity": "sha512-F+qYmH4z2s2FK+CxGj8moYcd1ekSIKH8ywkdqlOz88Dat35iB1DIYL11aILN46YSGMzQW/lbJNS307zBSDN5Ig==",
+ "license": "GPL-3.0-or-later WITH Classpath-exception-2.0",
+ "optional": true,
+ "dependencies": {
+ "ws": "^8.8.1"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/undici-types": {
+ "version": "6.20.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
+ "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
+ "license": "MIT"
+ },
+ "node_modules/web-streams-polyfill": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ }
+ }
+}
diff --git a/scripts/generate-signature/package.json b/scripts/generate-signature/package.json
new file mode 100644
index 0000000..93be3ba
--- /dev/null
+++ b/scripts/generate-signature/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "generate-signature",
+ "version": "1.0.0",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "description": "",
+ "dependencies": {
+ "@polkadot/api": "^15.5.2",
+ "@polkadot/keyring": "^13.3.1",
+ "@polkadot/types": "^15.5.2",
+ "@polkadot/util": "^13.3.1",
+ "@polkadot/wasm-crypto": "^7.4.1"
+ }
+}
Creating script
We'll create a light weight client for the Polkadot network. This script will allow us to interact with the Polkadot network using the Polkadot API.
Start local node:
At the root of the project, run the following command to start a local node:
./target/release/academy-pow --dev --tmp
Implement script
- Create
scripts/generate-signature/index.js
file.
cd scripts/generate-signature
touch index.js
- Create client
const { WsProvider } = require('@polkadot/api');
const wsProvider = new WsProvider('ws://127.0.0.1:9944'); // Replace with your endpoint
async function main() {
}
main().then(() => process.exit(0)).catch(error => {
console.error(error);
process.exit(1);
});
- Define keyring and data structure
const { WsProvider } = require('@polkadot/api');
+const Keyring = require('@polkadot/keyring').default;
+const { ApiPromise } = require('@polkadot/api');
const wsProvider = new WsProvider('ws://127.0.0.1:9944'); // Replace with your endpoint
async function main() {
+ // because our account address is in `sr25519` format
+ const keyring = new Keyring({ type: 'sr25519', ss58Format: 2 });
+ // Register types
+ const api = await ApiPromise.create({
+ types: {
+ TransactionInput: {
+ outpoint: 'H256', // Fixed 32 bytes
+ sigscript: 'H512' // Fixed 64 bytes
+ },
+ TransactionOutput: {
+ value: 'u128', // 16 bytes
+ pubkey: 'H256' // Fixed 32 bytes
+ },
+ Transaction: {
+ inputs: 'Vec<TransactionInput>', // Vec type
+ outputs: 'Vec<TransactionOutput>' // Vec type
+ }
+ },
+ provider: wsProvider, // Add the provider here
});
}
main().then(() => process.exit(0)).catch(error => {
console.error(error);
process.exit(1);
});
- Define signer
async function main() {
// ...
// create Alice based on the development seed. You can change to any other signer if you want
const alice = keyring.addFromUri('//Alice');
}
- Define transaction data
Scenarios:
- Alice address in sr25519 format:
0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
. - Bob address in sr25519 format:
0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48
. - Alice has an UTXO
0xc670c5f69081da78af400552edcafa3f0f31e84db1b50dd70776e0f87477b3dc
worth 100. - Alice wants to send 50 to Bob.
- Alice gets back 50.
async function main() {
// ...
const inputs = [{
// the latest UTXO hash of the account want to spent
outpoint: "0xc670c5f69081da78af400552edcafa3f0f31e84db1b50dd70776e0f87477b3dc",
// default simple sigscript
sigscript: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
}]
const outputs = [
{
// Value to spend
value: "50",
// Bob pubkey
pubkey: "0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48",
},
{
// Value to spend
value: "50",
// Alice pubkey
pubkey: "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
},
]
}
- Encode data then sign the data
// ...
const { u8aToHex } = require('@polkadot/util');
async function main() {
// ...
// Encode full transaction
const encodedTx = api.createType('Transaction', {
inputs: inputs,
outputs: outputs
}).toU8a();
// the encoded transaction in u8 array
const signature = alice.sign(encodedTx);
const isValid = alice.verify(encodedTx, signature, alice.publicKey);
if (isValid) {
console.log(`✅ Signature: ${u8aToHex(signature)}`);
} else {
console.log(`❌ Something went wrong! Please report to 👉 https://github.com/danielbui12/substrate-bitcoin-like-blockchain/issues/new`);
}
}
- Run the script
node index.js
2025-02-10 15:08:18 API/INIT: RPC methods not decorated: chainHead_v1_body, chainHead_v1_call, chainHead_v1_continue, chainHead_v1_follow, chainHead_v1_header, chainHead_v1_stopOperation, chainHead_v1_storage, chainHead_v1_unfollow, chainHead_v1_unpin, transactionWatch_v1_submitAndWatch, transactionWatch_v1_unwatch, transaction_v1_broadcast, transaction_v1_stop
✅ Signature: 0xe6d4b51e3a892a074eff4e1d7358b76c70c36f7956fb1c203d7c8e8504a51f469c7ce771646f7dd751cee91051b46b52508edfba7b318004a429ee7b255bbe8d
const { WsProvider } = require('@polkadot/api');
const Keyring = require('@polkadot/keyring').default;
const { ApiPromise } = require('@polkadot/api');
const { u8aToHex } = require('@polkadot/util');
const wsProvider = new WsProvider('ws://127.0.0.1:9944'); // Replace with your endpoint
async function main() {
// because our account address is in `sr25519` format
const keyring = new Keyring({ type: 'sr25519', ss58Format: 2 });
// Register types
const api = await ApiPromise.create({
types: {
TransactionInput: {
outpoint: 'H256', // Fixed 32 bytes
sigscript: 'H512' // Fixed 64 bytes
},
TransactionOutput: {
value: 'u128', // 16 bytes
pubkey: 'H256' // Fixed 32 bytes
},
Transaction: {
inputs: 'Vec<TransactionInput>', // Vec type
outputs: 'Vec<TransactionOutput>' // Vec type
}
},
provider: wsProvider, // Add the provider here
});
// create Alice based on the development seed. You can change to any other signer if you want
const alice = keyring.addFromUri('//Alice');
const inputs = [{
// the latest UTXO hash of the account want to spent
outpoint: "0xc670c5f69081da78af400552edcafa3f0f31e84db1b50dd70776e0f87477b3dc",
// default simple sigscript
sigscript: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
}]
const outputs = [
{
// Value to spend
value: "50",
// Bob pubkey
pubkey: "0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48",
},
{
// Value to spend
value: "50",
// Alice pubkey
pubkey: "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
},
]
// Encode full transaction
const encodedTx = api.createType('Transaction', {
inputs: inputs,
outputs: outputs
}).toU8a();
// the encoded transaction in u8 array
const signature = alice.sign(encodedTx);
const isValid = alice.verify(encodedTx, signature, alice.publicKey);
if (isValid) {
console.log(`✅ Signature: ${u8aToHex(signature)}`);
} else {
console.log(`❌ Something went wrong! Please report to 👉 https://github.com/danielbui12/substrate-bitcoin-like-blockchain/issues/new`);
}
}
main().then(() => process.exit(0)).catch(error => {
console.error(error);
process.exit(1);
});
diff --git a/scripts/generate-signature/index.js b/scripts/generate-signature/index.js
new file mode 100644
index 0000000..30c4d75
--- /dev/null
+++ b/scripts/generate-signature/index.js
@@ -0,0 +1,73 @@
+const { WsProvider } = require('@polkadot/api');
+const Keyring = require('@polkadot/keyring').default;
+const { ApiPromise } = require('@polkadot/api');
+const { u8aToHex } = require('@polkadot/util');
+
+const wsProvider = new WsProvider('ws://127.0.0.1:9944'); // Replace with your endpoint
+
+async function main() {
+ // because our account address is in `sr25519` format
+ const keyring = new Keyring({ type: 'sr25519', ss58Format: 2 });
+ // Register types
+ const api = await ApiPromise.create({
+ types: {
+ TransactionInput: {
+ outpoint: 'H256', // Fixed 32 bytes
+ sigscript: 'H512' // Fixed 64 bytes
+ },
+ TransactionOutput: {
+ value: 'u128', // 16 bytes
+ pubkey: 'H256' // Fixed 32 bytes
+ },
+ Transaction: {
+ inputs: 'Vec<TransactionInput>', // Vec type
+ outputs: 'Vec<TransactionOutput>' // Vec type
+ }
+ },
+ provider: wsProvider, // Add the provider here
+ });
+
+ // create Alice based on the development seed. You can change to any other signer if you want
+ const alice = keyring.addFromUri('//Alice');
+
+ const inputs = [{
+ // the latest UTXO hash of the account want to spent
+ outpoint: "0xc670c5f69081da78af400552edcafa3f0f31e84db1b50dd70776e0f87477b3dc",
+ // default simple sigscript
+ sigscript: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ }]
+ const outputs = [
+ {
+ // Value to spend
+ value: "50",
+ // Bob pubkey
+ pubkey: "0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48",
+ },
+ {
+ // Value to spend
+ value: "50",
+ // Alice pubkey
+ pubkey: "0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d",
+ },
+ ]
+ // Encode full transaction
+ const encodedTx = api.createType('Transaction', {
+ inputs: inputs,
+ outputs: outputs
+ }).toU8a();
+
+ // the encoded transaction in u8 array
+ const signature = alice.sign(encodedTx);
+ const isValid = alice.verify(encodedTx, signature, alice.publicKey);
+
+ if (isValid) {
+ console.log(`✅ Signature: ${u8aToHex(signature)}`);
+ } else {
+ console.log(`❌ Something went wrong! Please report to 👉 https://github.com/danielbui12/substrate-bitcoin-like-blockchain/issues/new`);
+ }
+}
+
+main().then(() => process.exit(0)).catch(error => {
+ console.error(error);
+ process.exit(1);
+});
\ No newline at end of file
Wrap up
Wow, you've made it all the way to the end!
Congratulations on completing all steps! 🎉 You've done an amazing job and you're all done! 🙌
Start hacking by following demo usage tutorial!
Demo usage
[!IMPORTANT] Please follow up strictly these value to have best experience 😉.
Address
- Powerful tool to convert
SS58 account
tosr25519::Pubkey
: https://polkadot.subscan.io/tools/format_transform - Alice: from
5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
to0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
- Bob: from
5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty
to0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48
Scenario
- Initially, Alice has a UTXO 1,125,899,906,842,624.
- She transfers to BOB a UTXO 842,624; but she doesn't have a UTXO $842,624. So she has to use her 1,125,899,906,842,624.
TransactionInput
containsoutpoint
is UTXO 1,125,899,906,842,624;sigscript
is signed data of "simple transaction" (refer toruntime/src/utxo.rs:354
).- There will be 2
TransactionOutput
s:TransactionOutput
containsvalue
is UTXO 842,624;pubkey
is Bob addressTransactionOutput
containsvalue
is UTXO 1,125,899,906,000,000;pubkey
is Alice address
flowchart LR AliceBefore[Alice <br/> UTXO 1,125,899,906,842,624] --> T{Transaction} T -->|Change| AliceAfter[Alice <br/> UTXO 842,624] T -->|Receive| Bob[Bob <br/> UTXO 1,125,899,906,000,000]
Steps
- Navigate to: https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/extrinsics
Check balance
- Switch to Chain State, select
utxo
,utxoOf
- Input:
0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
- Press + button
- Verify Alice has a UTXO hash
utxo.utxoOf: Option<H256>
0xc670c5f69081da78af400552edcafa3f0f31e84db1b50dd70776e0f87477b3dc
- Then select
utxo
,utxoStore
- Input:
0xc670c5f69081da78af400552edcafa3f0f31e84db1b50dd70776e0f87477b3dc
- Press + button
- Verify Alice has a UTXO value
utxo.utxoStore: Option<AcademyPowRuntimeUtxoTransactionOutput>
{
value: 1,125,899,906,842,624
pubkey: 0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
}
Alice transfers UTXO 842,624 to Bob, get back UXTO 1,125,899,906,000,000
Input
-
inputs[0].outpoint
: 0xc670c5f69081da78af400552edcafa3f0f31e84db1b50dd70776e0f87477b3dc -
inputs[0].sigscript
: 0xa23674686467cf6a3d755987276e1dbb71b4800ff99f2721e7d4fd52774fe43086ed5a385b5cde80a346be5eb7afd3e95ef1dd9b9069deee889cd432891ba481 -
outputs[0].value
: 842624 -
outputs[0].pubkey
:0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48
-
outputs[1].value
: 1125899906000000 -
outputs[1].pubkey
:0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d
Then submit unsigned transaction
Events
- New UTXO hash of Bob:
0x60bfe689ea5d2f0e2380a146289067465a3608b9ef20ef9e152cf501c0040dba
- New UTXO hash of Alice:
0x426e4f172479a674b4c044c34a77453bdf4ddbdf8b3993d586a05d9cffe57bd3
Check balance
- Switch to Chain State, select
utxo
,utxoStore
Alice:
- Input:
0x426e4f172479a674b4c044c34a77453bdf4ddbdf8b3993d586a05d9cffe57bd3
- Press + button
- Verify Alice has a UTXO 1,125,899,906,000,000
Bob:
- Input:
0x60bfe689ea5d2f0e2380a146289067465a3608b9ef20ef9e152cf501c0040dba
- Press + button
- Verify Bob has a UTXO 842,624
Custom your own
[!NOTE] If you would love to play in your own way, refer to
scripts/generate-signature/index.js
to generate your own custom signed data!
Run multiple nodes
# Start the first local blockchain node using Ferdie account,
# using md5 algorithm
./target/release/academy-pow \
--mining-algo md5 \
--base-path ./validators/ferdie \
--dev \
--ferdie \
--port 30333 \
--rpc-port 9944 \
--node-key 0000000000000000000000000000000000000000000000000000000000000001 \
--no-prometheus \
--validator
# Start the second local blockchain node using Eve account,
# using sha3 algorithm
./target/release/academy-pow \
--mining-algo sha3 \
--base-path ./validators/eve \
--dev \
--eve \
--port 30334 \
--rpc-port 9945 \
--no-prometheus \
--bootnodes /ip4/127.0.0.1/tcp/30333/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp \
--validator
Then you can see Ferdie and Eve are continuously producing blocks.
Purge chain
./target/release/academy-pow purge-chain --base-path ./validators/ferdie --dev
./target/release/academy-pow purge-chain --base-path ./validators/eve --dev
More Help
academy-pow --help